summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/compiler/cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/jaegertracing/thrift/compiler/cpp
parentInitial commit. (diff)
downloadceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz
ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/jaegertracing/thrift/compiler/cpp')
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/CMakeLists.txt123
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/Makefile.am127
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/README.md175
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/coding_standards.md4
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/compiler.sln20
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj251
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj.filters211
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/Makefile.am39
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp454
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.h14
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/common.cc65
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/common.h43
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc2592
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc4583
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cl_generator.cc558
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc4554
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc3261
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc774
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc2514
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc4011
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc1282
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.cc262
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.h452
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h106
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc3742
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc345
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc2981
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc1717
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc1084
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h240
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc5456
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc3294
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc2948
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc793
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc1138
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.cc3133
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.h160
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.cc3023
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.h160
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc1762
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h112
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc1683
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc2785
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc2781
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc1288
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rs_generator.cc3351
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc1056
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc3199
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc692
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc376
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/thrift-t_php_generator.o-a60a38e90
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/globals.h141
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.cc72
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.h47
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/main.cc1290
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/main.h119
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/parse.cc35
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_base_type.h117
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const.h50
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const_value.h204
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_container.h47
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_doc.h55
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum.h110
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h50
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_field.h136
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_function.h93
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_list.h41
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_map.h45
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_program.h416
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_scope.h206
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_service.h61
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_set.h43
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_struct.h165
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_type.h109
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc38
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.h68
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/platform.h50
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/thriftl.ll362
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/thrifty.yy1201
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/version.h.in29
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/src/thrift/windows/config.h51
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/CMakeLists.txt43
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/Makefile.am28
-rwxr-xr-xsrc/jaegertracing/thrift/compiler/cpp/test/bincat.sh3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/compiler/Included.thrift18
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/compiler/Including.thrift7
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/compiler/Single.thrift1
-rwxr-xr-xsrc/jaegertracing/thrift/compiler/cpp/test/compiler/staleness_check.py142
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/const1_return.thrift1
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum1_return.thrift2
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum2_return.thrift3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception1_return.thrift1
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception2_return.thrift3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service1_return.thrift1
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service2_return.thrift3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service3_return.thrift3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service4_return.thrift5
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct1_return.thrift1
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct2_return.thrift3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/typedef1_return.thrift1
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union1_return.thrift1
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union2_return.thrift3
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/CMakeLists.txt153
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/README.md88
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/catch/catch.hpp11508
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc339
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc237
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h34
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc209
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc74
-rw-r--r--src/jaegertracing/thrift/compiler/cpp/tests/tests_main.cc19
111 files changed, 94159 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/compiler/cpp/CMakeLists.txt b/src/jaegertracing/thrift/compiler/cpp/CMakeLists.txt
new file mode 100644
index 000000000..17dae4787
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/CMakeLists.txt
@@ -0,0 +1,123 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+cmake_minimum_required(VERSION 3.3)
+project("thrift-compiler" VERSION ${PACKAGE_VERSION})
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h)
+
+find_package(FLEX REQUIRED)
+find_package(BISON REQUIRED)
+
+# create directory for thrifty and thriftl
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/thrift/)
+
+# Create flex and bison files and build the lib parse static library
+BISON_TARGET(thrifty ${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/thrifty.yy ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.cc)
+FLEX_TARGET(thriftl ${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/thriftl.ll ${CMAKE_CURRENT_BINARY_DIR}/thrift/thriftl.cc)
+ADD_FLEX_BISON_DEPENDENCY(thriftl thrifty)
+
+set(parse_SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/thriftl.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.hh
+)
+
+add_library(parse STATIC ${parse_SOURCES})
+
+# Create the thrift compiler
+set(compiler_core
+ src/thrift/common.cc
+ src/thrift/generate/t_generator.cc
+ src/thrift/parse/t_typedef.cc
+ src/thrift/parse/parse.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h
+)
+
+set(thrift-compiler_SOURCES
+ src/thrift/main.cc
+ src/thrift/audit/t_audit.cpp
+)
+
+set(thrift_compiler_LANGS
+)
+
+# This macro adds an option THRIFT_COMPILER_${NAME}
+# that allows enabling or disabling certain languages
+macro(THRIFT_ADD_COMPILER name description initial)
+ string(TOUPPER "THRIFT_COMPILER_${name}" enabler)
+ set(src "src/thrift/generate/t_${name}_generator.cc")
+ option(${enabler} ${description} ${initial})
+ if(${enabler})
+ list(APPEND thrift-compiler_SOURCES ${src})
+ list(APPEND thrift_compiler_LANGS ${name})
+ endif()
+endmacro()
+
+# The following compiler can be enabled or disabled
+THRIFT_ADD_COMPILER(as3 "Enable compiler for ActionScript 3" ON)
+THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" ON)
+THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" ON)
+THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" ON)
+THRIFT_ADD_COMPILER(csharp "Enable compiler for C#" ON)
+THRIFT_ADD_COMPILER(d "Enable compiler for D" ON)
+THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" ON)
+THRIFT_ADD_COMPILER(delphi "Enable compiler for Delphi" ON)
+THRIFT_ADD_COMPILER(erl "Enable compiler for Erlang" ON)
+THRIFT_ADD_COMPILER(go "Enable compiler for Go" ON)
+THRIFT_ADD_COMPILER(gv "Enable compiler for GraphViz" ON)
+THRIFT_ADD_COMPILER(haxe "Enable compiler for Haxe" ON)
+THRIFT_ADD_COMPILER(hs "Enable compiler for Haskell" ON)
+THRIFT_ADD_COMPILER(html "Enable compiler for HTML Documentation" ON)
+THRIFT_ADD_COMPILER(java "Enable compiler for Java" ON)
+THRIFT_ADD_COMPILER(javame "Enable compiler for Java ME" ON)
+THRIFT_ADD_COMPILER(js "Enable compiler for JavaScript" ON)
+THRIFT_ADD_COMPILER(json "Enable compiler for JSON" ON)
+THRIFT_ADD_COMPILER(lua "Enable compiler for Lua" ON)
+THRIFT_ADD_COMPILER(netcore "Enable compiler for .NET Core" ON)
+THRIFT_ADD_COMPILER(netstd "Enable compiler for .NET Standard" ON)
+THRIFT_ADD_COMPILER(ocaml "Enable compiler for OCaml" ON)
+THRIFT_ADD_COMPILER(perl "Enable compiler for Perl" ON)
+THRIFT_ADD_COMPILER(php "Enable compiler for PHP" ON)
+THRIFT_ADD_COMPILER(py "Enable compiler for Python 2.0" ON)
+THRIFT_ADD_COMPILER(rb "Enable compiler for Ruby" ON)
+THRIFT_ADD_COMPILER(rs "Enable compiler for Rust" ON)
+THRIFT_ADD_COMPILER(st "Enable compiler for Smalltalk" ON)
+THRIFT_ADD_COMPILER(swift "Enable compiler for Cocoa Swift" ON)
+THRIFT_ADD_COMPILER(xml "Enable compiler for XML" ON)
+THRIFT_ADD_COMPILER(xsd "Enable compiler for XSD" ON)
+
+# Thrift is looking for include files in the src directory
+# we also add the current binary directory for generated files
+include_directories(${CMAKE_CURRENT_BINARY_DIR} src)
+
+list(APPEND thrift-compiler_SOURCES ${compiler_core})
+
+add_executable(thrift-compiler ${thrift-compiler_SOURCES})
+
+set_target_properties(thrift-compiler PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin/)
+set_target_properties(thrift-compiler PROPERTIES OUTPUT_NAME thrift)
+
+target_link_libraries(thrift-compiler parse)
+
+install(TARGETS thrift-compiler DESTINATION bin)
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+endif()
diff --git a/src/jaegertracing/thrift/compiler/cpp/Makefile.am b/src/jaegertracing/thrift/compiler/cpp/Makefile.am
new file mode 100644
index 000000000..91801c6ab
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/Makefile.am
@@ -0,0 +1,127 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+AUTOMAKE_OPTIONS = subdir-objects nostdinc
+
+SUBDIRS = src .
+if WITH_TESTS
+SUBDIRS += test
+endif
+
+bin_PROGRAMS = thrift
+
+thrift_OBJDIR = obj
+
+thrift_SOURCES = src/thrift/audit/t_audit.cpp \
+ src/thrift/audit/t_audit.h \
+ src/thrift/common.cc \
+ src/thrift/common.h \
+ src/thrift/generate/t_generator.cc \
+ src/thrift/generate/t_generator.h \
+ src/thrift/generate/t_generator_registry.h \
+ src/thrift/generate/t_html_generator.h \
+ src/thrift/generate/t_oop_generator.h \
+ src/thrift/globals.h \
+ src/thrift/logging.h \
+ src/thrift/main.cc \
+ src/thrift/main.h \
+ src/thrift/parse/parse.cc \
+ src/thrift/parse/t_base_type.h \
+ src/thrift/parse/t_const.h \
+ src/thrift/parse/t_const_value.h \
+ src/thrift/parse/t_container.h \
+ src/thrift/parse/t_doc.h \
+ src/thrift/parse/t_enum.h \
+ src/thrift/parse/t_enum_value.h \
+ src/thrift/parse/t_field.h \
+ src/thrift/parse/t_function.h \
+ src/thrift/parse/t_list.h \
+ src/thrift/parse/t_map.h \
+ src/thrift/parse/t_program.h \
+ src/thrift/parse/t_scope.h \
+ src/thrift/parse/t_service.h \
+ src/thrift/parse/t_set.h \
+ src/thrift/parse/t_struct.h \
+ src/thrift/parse/t_type.h \
+ src/thrift/parse/t_typedef.cc \
+ src/thrift/parse/t_typedef.h \
+ src/thrift/platform.h
+
+# Specific client generator source
+thrift_SOURCES += src/thrift/generate/t_as3_generator.cc \
+ src/thrift/generate/t_c_glib_generator.cc \
+ src/thrift/generate/t_cl_generator.cc \
+ src/thrift/generate/t_cpp_generator.cc \
+ src/thrift/generate/t_csharp_generator.cc \
+ src/thrift/generate/t_d_generator.cc \
+ src/thrift/generate/t_dart_generator.cc \
+ src/thrift/generate/t_delphi_generator.cc \
+ src/thrift/generate/t_erl_generator.cc \
+ src/thrift/generate/t_go_generator.cc \
+ src/thrift/generate/t_gv_generator.cc \
+ src/thrift/generate/t_haxe_generator.cc \
+ src/thrift/generate/t_hs_generator.cc \
+ src/thrift/generate/t_html_generator.cc \
+ src/thrift/generate/t_java_generator.cc \
+ src/thrift/generate/t_javame_generator.cc \
+ src/thrift/generate/t_js_generator.cc \
+ src/thrift/generate/t_json_generator.cc \
+ src/thrift/generate/t_lua_generator.cc \
+ src/thrift/generate/t_netcore_generator.cc \
+ src/thrift/generate/t_netcore_generator.h \
+ src/thrift/generate/t_netstd_generator.cc \
+ src/thrift/generate/t_netstd_generator.h \
+ src/thrift/generate/t_ocaml_generator.cc \
+ src/thrift/generate/t_perl_generator.cc \
+ src/thrift/generate/t_php_generator.cc \
+ src/thrift/generate/t_py_generator.cc \
+ src/thrift/generate/t_rb_generator.cc \
+ src/thrift/generate/t_rs_generator.cc \
+ src/thrift/generate/t_st_generator.cc \
+ src/thrift/generate/t_swift_generator.cc \
+ src/thrift/generate/t_xml_generator.cc \
+ src/thrift/generate/t_xsd_generator.cc
+
+thrift_CPPFLAGS = -I$(srcdir)/src
+thrift_CXXFLAGS = -Wall -Wextra -pedantic -Werror
+thrift_LDADD = @LEXLIB@ src/thrift/libparse.a
+
+WINDOWS_DIST = \
+ compiler.sln \
+ compiler.vcxproj \
+ compiler.vcxproj.filters
+
+EXTRA_DIST = \
+ coding_standards.md \
+ README.md \
+ CMakeLists.txt \
+ test \
+ $(WINDOWS_DIST)
+
+clean-local:
+ $(RM) version.h
+
+src/thrift/main.cc: src/thrift/version.h
+
+style-local:
+ $(CPPSTYLE_CMD)
diff --git a/src/jaegertracing/thrift/compiler/cpp/README.md b/src/jaegertracing/thrift/compiler/cpp/README.md
new file mode 100644
index 000000000..5c04cd869
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/README.md
@@ -0,0 +1,175 @@
+# Build Thrift IDL compiler using CMake
+
+<!-- TOC -->
+
+- [Build Thrift IDL compiler using CMake](#build-thrift-idl-compiler-using-cmake)
+ - [Build on Unix-like System](#build-on-unix-like-system)
+ - [Prerequisites](#prerequisites)
+ - [Build using CMake](#build-using-cmake)
+ - [Build with Eclipse IDE](#build-with-eclipse-ide)
+ - [Build with XCode IDE in MacOS](#build-with-xcode-ide-in-macos)
+ - [Usage of other IDEs](#usage-of-other-ides)
+ - [Build on Windows](#build-on-windows)
+ - [Prerequisites](#prerequisites-1)
+ - [Build using Git Bash](#build-using-git-bash)
+ - [Using Visual Studio and Win flex-bison](#using-visual-studio-and-win-flex-bison)
+ - [Cross compile using mingw32 and generate a Windows Installer with CPack](#cross-compile-using-mingw32-and-generate-a-windows-installer-with-cpack)
+- [Other cases](#other-cases)
+ - [Building the Thrift IDL compiler in Windows without CMake](#building-the-thrift-idl-compiler-in-windows-without-cmake)
+- [Unit tests for compiler](#unit-tests-for-compiler)
+ - [Using boost test](#using-boost-test)
+ - [Using Catch C++ test library](#using-catch-c-test-library)
+- [Have a Happy free time and holidays](#have-a-happy-free-time-and-holidays)
+
+<!-- /TOC -->
+
+## Build on Unix-like System
+
+### Prerequisites
+- Install CMake
+- Install flex and bison
+
+### Build using CMake
+
+- Go to **thrift\compiler\cpp**
+- Use the following steps to build using cmake:
+
+```
+mkdir cmake-build && cd cmake-build
+cmake ..
+make
+```
+
+#### Build with Eclipse IDE
+
+- Go to **thrift\compiler\cpp**
+- Use the following steps to build using cmake:
+
+```
+mkdir cmake-ec && cd cmake-ec
+cmake -G "Eclipse CDT4 - Unix Makefiles" ..
+make
+```
+
+Now open the folder cmake-ec using eclipse.
+
+#### Build with XCode IDE in MacOS
+
+- Install/update flex, bison and cmake with brew
+
+```
+brew install cmake
+brew install bison
+```
+
+- Go to **thrift\compiler\cpp**
+- Run commands in command line:
+
+```
+mkdir cmake-build && cd cmake-build
+cmake -G "Xcode" ..
+cmake --build .
+```
+
+#### Usage of other IDEs
+
+Please check list of supported IDE
+
+```
+cmake --help
+```
+
+## Build on Windows
+
+### Prerequisites
+- Install CMake - https://cmake.org/download/
+- In case if you want to build without Git Bash - install winflexbison - https://sourceforge.net/projects/winflexbison/
+- In case if you want to build with Visual Studio - install Visual Studio
+ - Better to use the latest stable Visual Studio Community Edition - https://www.visualstudio.com/vs/whatsnew/ (ensure that you installed workload "Desktop Development with C++" for VS2017) - Microsoft added some support for CMake and improving it in Visual Studio
+
+### Build using Git Bash
+
+Git Bash provides flex and bison
+
+- Go to **thrift\compiler\cpp**
+- Use the following steps to build using cmake:
+
+```
+mkdir cmake-vs && cd cmake-vs
+cmake -DWITH_SHARED_LIB=off ..
+cmake --build .
+```
+
+### Using Visual Studio and Win flex-bison
+
+- Generate a Visual Studio project for version of Visual Studio which you have (**cmake --help** can show list of supportable VS versions):
+- Run commands in command line:
+```
+mkdir cmake-vs
+cd cmake-vs
+cmake -G "Visual Studio 15 2017" ..
+```
+- Now open the folder cmake-vs using Visual Studio.
+
+### Cross compile using mingw32 and generate a Windows Installer with CPack
+
+```
+mkdir cmake-mingw32 && cd cmake-mingw32
+cmake -DCMAKE_TOOLCHAIN_FILE=../build/cmake/mingw32-toolchain.cmake -DBUILD_COMPILER=ON -DBUILD_LIBRARIES=OFF -DBUILD_TESTING=OFF ..
+cpack
+```
+
+# Other cases
+
+## Building the Thrift IDL compiler in Windows without CMake
+
+If you don't want to use CMake you can use the already available Visual Studio 2010 solution.
+
+The Visual Studio project contains pre-build commands to generate the thriftl.cc, thrifty.cc and thrifty.hh files which are necessary to build the compiler.
+
+These depend on bison, flex and their dependencies to work properly.
+
+Download flex & bison as described above.
+
+Place these binaries somewhere in the path and rename win_flex.exe and win_bison.exe to flex.exe and bison.exe respectively.
+
+If this doesn't work on a system, try these manual pre-build steps.
+
+Open compiler.sln and remove the Pre-build commands under the project's: Properties -> Build Events -> Pre-Build Events.
+
+From a command prompt:
+```
+cd thrift/compiler/cpp
+flex -o src\thrift\thriftl.cc src\thrift\thriftl.ll
+```
+In the generated thriftl.cc, comment out #include <unistd.h>
+
+Place a copy of bison.simple in thrift/compiler/cpp
+```
+bison -y -o "src/thrift/thrifty.cc" --defines src/thrift/thrifty.yy
+move src\thrift\thrifty.cc.hh src\thrift\thrifty.hh
+```
+
+Bison might generate the yacc header file "thrifty.cc.h" with just one h ".h" extension; in this case you'll have to rename to "thrifty.h".
+
+```
+move src\thrift\version.h.in src\thrift\version.h
+```
+
+Download inttypes.h from the interwebs and place it in an include path
+location (e.g. thrift/compiler/cpp/src).
+
+Build the compiler in Visual Studio.
+
+# Unit tests for compiler
+
+## Using boost test
+- pls check **test** folder
+
+## Using Catch C++ test library
+
+Added generic way to cover code by tests for many languages (you just need to make a correct header file for generator for your language - example in **netcore** implementation)
+
+- pls check **tests** folder
+
+# Have a Happy free time and holidays
diff --git a/src/jaegertracing/thrift/compiler/cpp/coding_standards.md b/src/jaegertracing/thrift/compiler/cpp/coding_standards.md
new file mode 100644
index 000000000..ea089467e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/coding_standards.md
@@ -0,0 +1,4 @@
+## Compiler Coding Standards
+
+ * When making small change / bugfix - follow style as seen in nearby code.
+ * When making major refactor and / or adding new feature - follow style for C++ library \ No newline at end of file
diff --git a/src/jaegertracing/thrift/compiler/cpp/compiler.sln b/src/jaegertracing/thrift/compiler/cpp/compiler.sln
new file mode 100644
index 000000000..94961aaef
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/compiler.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "compiler", "compiler.vcxproj", "{89975A1A-F799-4556-98B8-64E30AB39A90}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {89975A1A-F799-4556-98B8-64E30AB39A90}.Debug|Win32.ActiveCfg = Debug|Win32
+ {89975A1A-F799-4556-98B8-64E30AB39A90}.Debug|Win32.Build.0 = Debug|Win32
+ {89975A1A-F799-4556-98B8-64E30AB39A90}.Release|Win32.ActiveCfg = Release|Win32
+ {89975A1A-F799-4556-98B8-64E30AB39A90}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj b/src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj
new file mode 100644
index 000000000..06c4eb4d8
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj
@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\thrift\audit\t_audit.h" />
+ <ClInclude Include="src\thrift\common.h" />
+ <ClInclude Include="src\thrift\generate\t_generator.h" />
+ <ClInclude Include="src\thrift\generate\t_generator_registry.h" />
+ <ClInclude Include="src\thrift\generate\t_oop_generator.h" />
+ <ClInclude Include="src\thrift\generate\t_html_generator.h" />
+ <ClInclude Include="src\thrift\globals.h" />
+ <ClInclude Include="src\thrift\main.h" />
+ <ClInclude Include="src\thrift\parse\t_base_type.h" />
+ <ClInclude Include="src\thrift\parse\t_const.h" />
+ <ClInclude Include="src\thrift\parse\t_const_value.h" />
+ <ClInclude Include="src\thrift\parse\t_container.h" />
+ <ClInclude Include="src\thrift\parse\t_doc.h" />
+ <ClInclude Include="src\thrift\parse\t_enum.h" />
+ <ClInclude Include="src\thrift\parse\t_enum_value.h" />
+ <ClInclude Include="src\thrift\parse\t_field.h" />
+ <ClInclude Include="src\thrift\parse\t_function.h" />
+ <ClInclude Include="src\thrift\parse\t_list.h" />
+ <ClInclude Include="src\thrift\parse\t_map.h" />
+ <ClInclude Include="src\thrift\parse\t_program.h" />
+ <ClInclude Include="src\thrift\parse\t_scope.h" />
+ <ClInclude Include="src\thrift\parse\t_service.h" />
+ <ClInclude Include="src\thrift\parse\t_set.h" />
+ <ClInclude Include="src\thrift\parse\t_struct.h" />
+ <ClInclude Include="src\thrift\parse\t_type.h" />
+ <ClInclude Include="src\thrift\parse\t_typedef.h" />
+ <ClInclude Include="src\thrift\platform.h" />
+ <ClInclude Include="src\thrift\thrifty.hh" />
+ <ClInclude Include="src\thrift\windows\config.h" />
+ <ClInclude Include="src\thrift\version.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\thrift\audit\t_audit.cpp" />
+ <ClCompile Include="src\thrift\common.cc" />
+ <ClCompile Include="src\thrift\generate\t_as3_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_c_glib_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_cl_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_cpp_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_csharp_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_d_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_dart_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_delphi_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_erl_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_go_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_gv_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_haxe_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_hs_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_html_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_java_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_javame_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_js_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_json_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_lua_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_netcore_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_netstd_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_ocaml_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_perl_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_php_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_py_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_rb_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_rs_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_st_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_swift_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_xml_generator.cc" />
+ <ClCompile Include="src\thrift\generate\t_xsd_generator.cc" />
+ <ClCompile Include="src\thrift\main.cc" />
+ <ClCompile Include="src\thrift\parse\parse.cc" />
+ <ClCompile Include="src\thrift\parse\t_typedef.cc" />
+ <ClCompile Include="src\thrift\thriftl.cc" />
+ <ClCompile Include="src\thrift\thrifty.cc" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="src\thrift\thriftl.ll" />
+ <None Include="src\thrift\thrifty.yy" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{89975A1A-F799-4556-98B8-64E30AB39A90}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>compiler</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath)</IncludePath>
+ <TargetName>thrift</TargetName>
+ <ExecutablePath>$(ExecutablePath);C:\Program Files (x86)\Git\bin</ExecutablePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath)</IncludePath>
+ <TargetName>thrift</TargetName>
+ <ExecutablePath>$(ExecutablePath);C:\Program Files (x86)\Git\bin</ExecutablePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath)</IncludePath>
+ <TargetName>thrift</TargetName>
+ <ExecutablePath>$(ExecutablePath);C:\Program Files (x86)\Git\bin</ExecutablePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <IncludePath>$(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath)</IncludePath>
+ <TargetName>thrift</TargetName>
+ <ExecutablePath>$(ExecutablePath);C:\Program Files (x86)\Git\bin</ExecutablePath>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ForcedIncludeFiles>thrift\windows\config.h</ForcedIncludeFiles>
+ <CompileAs>CompileAsCpp</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <PreBuildEvent>
+ <Command>flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll &amp;&amp; bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy</Command>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ForcedIncludeFiles>thrift\windows\config.h</ForcedIncludeFiles>
+ <CompileAs>CompileAsCpp</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <PreBuildEvent>
+ <Command>flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll &amp;&amp; bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy</Command>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ForcedIncludeFiles>thrift\windows\config.h</ForcedIncludeFiles>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <PreBuildEvent>
+ <Command>flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll &amp;&amp; bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy</Command>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ForcedIncludeFiles>thrift\windows\config.h</ForcedIncludeFiles>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <PreBuildEvent>
+ <Command>flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll &amp;&amp; bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy</Command>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj.filters b/src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj.filters
new file mode 100644
index 000000000..5a575894f
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/compiler.vcxproj.filters
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="src\audit\t_audit.h" />
+ <ClInclude Include="src\generate\t_generator.h">
+ <Filter>generate</Filter>
+ </ClInclude>
+ <ClInclude Include="src\generate\t_generator_registry.h">
+ <Filter>generate</Filter>
+ </ClInclude>
+ <ClInclude Include="src\generate\t_oop_generator.h">
+ <Filter>generate</Filter>
+ </ClInclude>
+ <ClInclude Include="src\generate\t_html_generator.h">
+ <Filter>generate</Filter>
+ </ClInclude>
+ <ClInclude Include="src\globals.h" />
+ <ClInclude Include="src\main.h" />
+ <ClInclude Include="src\parse\t_base_type.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_const.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_const_value.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_container.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_doc.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_enum.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_enum_value.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_field.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_function.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_list.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_map.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_program.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_scope.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_service.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_set.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_struct.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_type.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\parse\t_typedef.h">
+ <Filter>parse</Filter>
+ </ClInclude>
+ <ClInclude Include="src\platform.h" />
+ <ClInclude Include="src\thrifty.hh" />
+ <ClInclude Include="src\windows\config.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ <ClInclude Include="src\windows\version.h">
+ <Filter>windows</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="windows">
+ <UniqueIdentifier>{ae9d0a15-57ae-4f01-87a4-81f790249b83}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="parse">
+ <UniqueIdentifier>{5df016bb-591b-420a-a535-4330d9187fbf}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="generate">
+ <UniqueIdentifier>{b5c626af-afa5-433c-8e10-ee734533cb68}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\audit\t_audit.cpp"/>
+ <ClCompile Include="src\generate\t_as3_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_cocoa_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_cpp_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_csharp_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_c_glib_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_d_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_dart_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_delphi_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_erl_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_go_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_gv_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_haxe_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_hs_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_html_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_javame_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_java_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_js_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_ocaml_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_perl_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_php_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_py_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_rb_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_netcore_generator.h">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_netstd_generator.h">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_netcore_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_netstd_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_rs_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_st_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_swift_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_xsd_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_xml_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\main.cc" />
+ <ClCompile Include="src\parse\parse.cc">
+ <Filter>parse</Filter>
+ </ClCompile>
+ <ClCompile Include="src\thriftl.cc" />
+ <ClCompile Include="src\thrifty.cc" />
+ <ClCompile Include="src\parse\t_typedef.cc">
+ <Filter>parse</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_json_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ <ClCompile Include="src\generate\t_lua_generator.cc">
+ <Filter>generate</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="src\thriftl.ll" />
+ <None Include="src\thrifty.yy" />
+ </ItemGroup>
+</Project>
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/Makefile.am b/src/jaegertracing/thrift/compiler/cpp/src/Makefile.am
new file mode 100644
index 000000000..0297708e1
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/Makefile.am
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+AUTOMAKE_OPTIONS = subdir-objects nostdinc
+
+AM_YFLAGS = -d
+
+BUILT_SOURCES = thrift/thrifty.cc
+
+noinst_LIBRARIES = thrift/libparse.a
+
+thrift_libparse_a_CPPFLAGS = -I$(srcdir)
+thrift_libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused
+
+thrift_libparse_a_SOURCES = thrift/thrifty.yy \
+ thrift/thriftl.ll
+
+clean-local:
+ $(RM) thrift/thriftl.cc thrift/thrifty.cc thrift/thrifty.h thrift/thrifty.hh
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp b/src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp
new file mode 100644
index 000000000..fbce15c77
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp
@@ -0,0 +1,454 @@
+
+#include <cassert>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string>
+#include <algorithm>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+
+// Careful: must include globals first for extern definitions
+#include "thrift/globals.h"
+
+#include "thrift/parse/t_program.h"
+#include "thrift/parse/t_scope.h"
+#include "thrift/parse/t_const.h"
+#include "thrift/parse/t_field.h"
+
+#include "thrift/version.h"
+
+#include "thrift/audit/t_audit.h"
+
+extern int g_warn;
+extern std::string g_curpath;
+extern bool g_return_failure;
+
+void thrift_audit_warning(int level, const char* fmt, ...) {
+ if (g_warn < level) {
+ return;
+ }
+ va_list args;
+ printf("[Thrift Audit Warning:%s] ", g_curpath.c_str());
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+}
+
+void thrift_audit_failure(const char* fmt, ...) {
+ va_list args;
+ fprintf(stderr, "[Thrift Audit Failure:%s] ", g_curpath.c_str());
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ g_return_failure = true;
+}
+
+void compare_namespace(t_program* newProgram, t_program* oldProgram)
+{
+ const std::map<std::string, std::string>& newNamespaceMap = newProgram->get_all_namespaces();
+ const std::map<std::string, std::string>& oldNamespaceMap = oldProgram->get_all_namespaces();
+
+ for(const auto & oldNamespaceMapIt : oldNamespaceMap)
+ {
+ auto newNamespaceMapIt = newNamespaceMap.find(oldNamespaceMapIt.first);
+ if(newNamespaceMapIt == newNamespaceMap.end())
+ {
+ thrift_audit_warning(1, "Language %s not found in new thrift file\n", (oldNamespaceMapIt.first).c_str());
+ }
+ else if((newNamespaceMapIt->second) != oldNamespaceMapIt.second)
+ {
+ thrift_audit_warning(1, "Namespace %s changed in new thrift file\n", (oldNamespaceMapIt.second).c_str());
+ }
+ }
+}
+
+void compare_enum_values(t_enum* newEnum,t_enum* oldEnum)
+{
+ const std::vector<t_enum_value*>& oldEnumValues = oldEnum->get_constants();
+ for(auto oldEnumValue : oldEnumValues)
+ {
+ int enumValue = oldEnumValue->get_value();
+ t_enum_value* newEnumValue = newEnum->get_constant_by_value(enumValue);
+ if(newEnumValue != nullptr)
+ {
+ std::string enumName = oldEnumValue->get_name();
+ if(enumName != newEnumValue->get_name())
+ {
+ thrift_audit_warning(1, "Name of the value %d changed in enum %s\n", enumValue, oldEnum->get_name().c_str());
+ }
+ }
+ else
+ {
+ thrift_audit_failure("Enum value %d missing in %s\n", enumValue, oldEnum->get_name().c_str());
+ }
+
+ }
+}
+
+void compare_enums(const std::vector<t_enum*>& newEnumList, const std::vector<t_enum*>& oldEnumList)
+{
+ std::map<std::string,t_enum*> newEnumMap;
+ std::vector<t_enum*>::const_iterator newEnumIt;
+ for(newEnumIt = newEnumList.begin(); newEnumIt != newEnumList.end(); newEnumIt++)
+ {
+ newEnumMap[(*newEnumIt)->get_name()] = *newEnumIt;
+ }
+ std::vector<t_enum*>::const_iterator oldEnumIt;
+ for(oldEnumIt = oldEnumList.begin(); oldEnumIt != oldEnumList.end(); oldEnumIt++)
+ {
+ std::map<std::string,t_enum*>::iterator newEnumMapIt;
+ newEnumMapIt = newEnumMap.find((*oldEnumIt)->get_name());
+
+ if(newEnumMapIt == newEnumMap.end())
+ {
+ thrift_audit_warning(1, "Enum %s not found in new thrift file\n",(*oldEnumIt)->get_name().c_str());
+ }
+ else
+ {
+ compare_enum_values(newEnumMapIt->second, *oldEnumIt);
+ }
+ }
+}
+
+//This function returns 'true' if the two arguements are of same types.
+//Returns false if they are of different type
+bool compare_type(t_type* newType, t_type* oldType)
+{
+ //Comparing names of two types will work when the newType and oldType are basic types or structs or enums.
+ //However, when they are containers, get_name() returns empty for which we have to compare the type of
+ //their elements as well.
+ if((newType->get_name()).empty() && (oldType->get_name()).empty())
+ {
+
+ if(newType->is_list() && oldType->is_list())
+ {
+ t_type* newElementType = ((t_list*)newType)->get_elem_type();
+ t_type* oldElementType = ((t_list*)oldType)->get_elem_type();
+ return compare_type(newElementType, oldElementType);
+ }
+ else if(newType->is_map() && oldType->is_map())
+ {
+ t_type* newKeyType = ((t_map*)newType)->get_key_type();
+ t_type* oldKeyType = ((t_map*)oldType)->get_key_type();
+
+ t_type* newValType = ((t_map*)newType)->get_val_type();
+ t_type* oldValType = ((t_map*)oldType)->get_val_type();
+
+ return (compare_type(newKeyType, oldKeyType) && compare_type(newValType, oldValType));
+ }
+ else if(newType->is_set() && oldType->is_set())
+ {
+ t_type* newElementType = ((t_set*)newType)->get_elem_type();
+ t_type* oldElementType = ((t_set*)oldType)->get_elem_type();
+ return compare_type(newElementType, oldElementType);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if(newType->get_name() == oldType->get_name())
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool compare_pair(std::pair<t_const_value*, t_const_value*> newMapPair, std::pair<t_const_value*, t_const_value*> oldMapPair)
+{
+ return compare_defaults(newMapPair.first, oldMapPair.first) && compare_defaults(newMapPair.second, oldMapPair.second);
+}
+
+// This function returns 'true' if the default values are same. Returns false if they are different.
+bool compare_defaults(t_const_value* newStructDefault, t_const_value* oldStructDefault)
+{
+ if(newStructDefault == nullptr && oldStructDefault == nullptr) return true;
+ else if(newStructDefault == nullptr && oldStructDefault != nullptr) return false;
+ else if (newStructDefault != nullptr && oldStructDefault == nullptr) return false;
+
+ if(newStructDefault->get_type() != oldStructDefault->get_type())
+ {
+ return false;
+ }
+
+ switch(newStructDefault->get_type())
+ {
+ case t_const_value::CV_INTEGER:
+ return (newStructDefault->get_integer() == oldStructDefault->get_integer());
+ case t_const_value::CV_DOUBLE:
+ return (newStructDefault->get_double() == oldStructDefault->get_double());
+ case t_const_value::CV_STRING:
+ return (newStructDefault->get_string() == oldStructDefault->get_string());
+ case t_const_value::CV_LIST:
+ {
+ const std::vector<t_const_value*>& oldDefaultList = oldStructDefault->get_list();
+ const std::vector<t_const_value*>& newDefaultList = newStructDefault->get_list();
+ bool defaultValuesCompare = (oldDefaultList.size() == newDefaultList.size());
+
+ return defaultValuesCompare && std::equal(newDefaultList.begin(), newDefaultList.end(), oldDefaultList.begin(), compare_defaults);
+ }
+ case t_const_value::CV_MAP:
+ {
+ const std::map<t_const_value*, t_const_value*, t_const_value::value_compare> newMap = newStructDefault->get_map();
+ const std::map<t_const_value*, t_const_value*, t_const_value::value_compare> oldMap = oldStructDefault->get_map();
+
+ bool defaultValuesCompare = (oldMap.size() == newMap.size());
+
+ return defaultValuesCompare && std::equal(newMap.begin(), newMap.end(), oldMap.begin(), compare_pair);
+ }
+ case t_const_value::CV_IDENTIFIER:
+ return (newStructDefault->get_identifier() == oldStructDefault->get_identifier());
+ default:
+ return false;
+ }
+
+}
+
+void compare_struct_field(t_field* newField, t_field* oldField, std::string oldStructName)
+{
+ t_type* newFieldType = newField->get_type();
+ t_type* oldFieldType = oldField->get_type();
+ if(!compare_type(newFieldType, oldFieldType))
+ {
+ thrift_audit_failure("Struct Field Type Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
+ }
+
+ // A Struct member can be optional if it is mentioned explicitly, or if it is assigned with default values.
+ bool newStructFieldOptional = (newField->get_req() != t_field::T_REQUIRED);
+ bool oldStructFieldOptional = (oldField->get_req() != t_field::T_REQUIRED);
+
+ if(newStructFieldOptional != oldStructFieldOptional)
+ {
+ thrift_audit_failure("Struct Field Requiredness Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
+ }
+ if(newStructFieldOptional || oldStructFieldOptional)
+ {
+ if(!compare_defaults(newField->get_value(), oldField->get_value()))
+ {
+ thrift_audit_warning(1, "Default value changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
+ }
+ }
+
+ std::string fieldName = newField->get_name();
+ if(fieldName != oldField->get_name())
+ {
+ thrift_audit_warning(1, "Struct field name changed for Id = %d in %s\n", newField->get_key(), oldStructName.c_str());
+ }
+
+}
+
+void compare_single_struct(t_struct* newStruct, t_struct* oldStruct, const std::string& oldStructName = std::string())
+{
+ std::string structName = oldStructName.empty() ? oldStruct->get_name() : oldStructName;
+ const std::vector<t_field*>& oldStructMembersInIdOrder = oldStruct->get_sorted_members();
+ const std::vector<t_field*>& newStructMembersInIdOrder = newStruct->get_sorted_members();
+ auto oldStructMemberIt = oldStructMembersInIdOrder.begin();
+ auto newStructMemberIt = newStructMembersInIdOrder.begin();
+
+ // Since we have the struct members in their ID order, comparing their IDs can be done by traversing the two member
+ // lists together.
+ while(!(oldStructMemberIt == oldStructMembersInIdOrder.end() && newStructMemberIt == newStructMembersInIdOrder.end()))
+ {
+ if(newStructMemberIt == newStructMembersInIdOrder.end() && oldStructMemberIt != oldStructMembersInIdOrder.end())
+ {
+ // A field ID has been removed from the end.
+ thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str());
+ oldStructMemberIt++;
+ }
+ else if(newStructMemberIt != newStructMembersInIdOrder.end() && oldStructMemberIt == oldStructMembersInIdOrder.end())
+ {
+ //New field ID has been added to the end.
+ if((*newStructMemberIt)->get_req() == t_field::T_REQUIRED)
+ {
+ thrift_audit_failure("Required Struct Field Added for Id = %d in %s \n", (*newStructMemberIt)->get_key(), structName.c_str());
+ }
+ newStructMemberIt++;
+ }
+ else if((*newStructMemberIt)->get_key() == (*oldStructMemberIt)->get_key())
+ {
+ //Field ID found in both structs. Compare field types, default values.
+ compare_struct_field(*newStructMemberIt, *oldStructMemberIt, structName);
+
+ newStructMemberIt++;
+ oldStructMemberIt++;
+ }
+ else if((*newStructMemberIt)->get_key() < (*oldStructMemberIt)->get_key())
+ {
+ //New Field Id is inserted in between
+ //Adding fields to struct is fine, but adding them in the middle is suspicious. Error!!
+ thrift_audit_failure("Struct field is added in the middle with Id = %d in %s\n", (*newStructMemberIt)->get_key(), structName.c_str());
+ newStructMemberIt++;
+ }
+ else if((*newStructMemberIt)->get_key() > (*oldStructMemberIt)->get_key())
+ {
+ //A field is deleted in newStruct.
+ thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str());
+ oldStructMemberIt++;
+ }
+
+ }
+}
+
+void compare_structs(const std::vector<t_struct*>& newStructList, const std::vector<t_struct*>& oldStructList)
+{
+ std::map<std::string,t_struct*> newStructMap;
+ std::vector<t_struct*>::const_iterator newStructListIt;
+ for(newStructListIt = newStructList.begin(); newStructListIt != newStructList.end(); newStructListIt++)
+ {
+ newStructMap[(*newStructListIt)->get_name()] = *newStructListIt;
+ }
+
+ std::vector<t_struct*>::const_iterator oldStructListIt;
+ for(oldStructListIt = oldStructList.begin(); oldStructListIt != oldStructList.end(); oldStructListIt++)
+ {
+ std::map<std::string, t_struct*>::iterator newStructMapIt;
+ newStructMapIt = newStructMap.find((*oldStructListIt)->get_name());
+ if(newStructMapIt == newStructMap.end())
+ {
+ thrift_audit_failure("Struct %s not found in new thrift file\n", (*oldStructListIt)->get_name().c_str());
+ }
+ else
+ {
+ compare_single_struct(newStructMapIt->second, *oldStructListIt);
+ }
+ }
+
+}
+
+void compare_single_function(t_function* newFunction, t_function* oldFunction)
+{
+ t_type* newFunctionReturnType = newFunction->get_returntype();
+
+ if(newFunction->is_oneway() != oldFunction->is_oneway())
+ {
+ thrift_audit_failure("Oneway attribute changed for function %s\n",oldFunction->get_name().c_str());
+ }
+ if(!compare_type(newFunctionReturnType, oldFunction->get_returntype()))
+ {
+ thrift_audit_failure("Return type changed for function %s\n",oldFunction->get_name().c_str());
+ }
+
+ //Compare function arguments.
+ compare_single_struct(newFunction->get_arglist(), oldFunction->get_arglist());
+ std::string exceptionName = oldFunction->get_name();
+ exceptionName += "_exception";
+ compare_single_struct(newFunction->get_xceptions(), oldFunction->get_xceptions(), exceptionName);
+}
+
+void compare_functions(const std::vector<t_function*>& newFunctionList, const std::vector<t_function*>& oldFunctionList)
+{
+ std::map<std::string, t_function*> newFunctionMap;
+ std::map<std::string, t_function*>::iterator newFunctionMapIt;
+ for(auto newFunctionIt : newFunctionList)
+ {
+ newFunctionMap[newFunctionIt->get_name()] = newFunctionIt;
+ }
+
+ for(auto oldFunctionIt : oldFunctionList)
+ {
+ newFunctionMapIt = newFunctionMap.find(oldFunctionIt->get_name());
+ if(newFunctionMapIt == newFunctionMap.end())
+ {
+ thrift_audit_failure("New Thrift File has missing function %s\n",oldFunctionIt->get_name().c_str());
+ continue;
+ }
+ else
+ {
+ //Function is found in both thrift files. Compare return type and argument list
+ compare_single_function(newFunctionMapIt->second, oldFunctionIt);
+ }
+ }
+
+}
+
+void compare_services(const std::vector<t_service*>& newServices, const std::vector<t_service*>& oldServices)
+{
+ std::vector<t_service*>::const_iterator oldServiceIt;
+
+ std::map<std::string, t_service*> newServiceMap;
+ for(auto newService : newServices)
+ {
+ newServiceMap[newService->get_name()] = newService;
+ }
+
+
+ for(oldServiceIt = oldServices.begin(); oldServiceIt != oldServices.end(); oldServiceIt++)
+ {
+ const std::string oldServiceName = (*oldServiceIt)->get_name();
+ auto newServiceMapIt = newServiceMap.find(oldServiceName);
+
+ if(newServiceMapIt == newServiceMap.end())
+ {
+ thrift_audit_failure("New Thrift file is missing a service %s\n", oldServiceName.c_str());
+ }
+ else
+ {
+ t_service* oldServiceExtends = (*oldServiceIt)->get_extends();
+ t_service* newServiceExtends = (newServiceMapIt->second)->get_extends();
+
+ if(oldServiceExtends == nullptr)
+ {
+ // It is fine to add extends. So if service in older thrift did not have any extends, we are fine.
+ // DO Nothing
+ }
+ else if(oldServiceExtends != nullptr && newServiceExtends == nullptr)
+ {
+ thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str());
+ }
+ else
+ {
+ std::string oldExtendsName = oldServiceExtends->get_name();
+ std::string newExtendsName = newServiceExtends->get_name();
+
+ if( newExtendsName != oldExtendsName)
+ {
+ thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str());
+ }
+ }
+
+ compare_functions((newServiceMapIt->second)->get_functions(), (*oldServiceIt)->get_functions());
+ }
+
+ }
+
+}
+
+void compare_consts(const std::vector<t_const*>& newConst, const std::vector<t_const*>& oldConst)
+{
+ std::vector<t_const*>::const_iterator newConstIt;
+ std::vector<t_const*>::const_iterator oldConstIt;
+
+ std::map<std::string, t_const*> newConstMap;
+
+ for(newConstIt = newConst.begin(); newConstIt != newConst.end(); newConstIt++)
+ {
+ newConstMap[(*newConstIt)->get_name()] = *newConstIt;
+ }
+
+ std::map<std::string, t_const*>::const_iterator newConstMapIt;
+ for(oldConstIt = oldConst.begin(); oldConstIt != oldConst.end(); oldConstIt++)
+ {
+ newConstMapIt = newConstMap.find((*oldConstIt)->get_name());
+ if(newConstMapIt == newConstMap.end())
+ {
+ thrift_audit_warning(1, "Constants Missing %s \n", ((*oldConstIt)->get_name()).c_str());
+ }
+ else if(!compare_type((newConstMapIt->second)->get_type(), (*oldConstIt)->get_type()))
+ {
+ thrift_audit_warning(1, "Constant %s is of different type \n", ((*oldConstIt)->get_name()).c_str());
+ }
+ else if(!compare_defaults((newConstMapIt->second)->get_value(), (*oldConstIt)->get_value()))
+ {
+ thrift_audit_warning(1, "Constant %s has different value\n", ((*oldConstIt)->get_name()).c_str());
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.h
new file mode 100644
index 000000000..be79e3124
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/audit/t_audit.h
@@ -0,0 +1,14 @@
+#ifndef T_AUDIT_H
+#define T_AUDIT_H
+
+void compare_namespace(t_program* newProgram, t_program* oldProgram);
+void compare_enums(const std::vector<t_enum*>& newEnumList,
+ const std::vector<t_enum*>& oldEnumList);
+bool compare_defaults(t_const_value* newStructDefault, t_const_value* oldStructDefault);
+void compare_structs(const std::vector<t_struct*>& newStructList,
+ const std::vector<t_struct*>& oldStructList);
+void compare_services(const std::vector<t_service*>& newServices,
+ const std::vector<t_service*>& oldServices);
+void compare_consts(const std::vector<t_const*>& newConst, const std::vector<t_const*>& oldConst);
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/common.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/common.cc
new file mode 100644
index 000000000..fb1832daf
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/common.cc
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include "thrift/common.h"
+#include "thrift/parse/t_base_type.h"
+
+t_type* g_type_void;
+t_type* g_type_string;
+t_type* g_type_binary;
+t_type* g_type_slist;
+t_type* g_type_bool;
+t_type* g_type_i8;
+t_type* g_type_i16;
+t_type* g_type_i32;
+t_type* g_type_i64;
+t_type* g_type_double;
+
+void initGlobals() {
+ g_type_void = new t_base_type("void", t_base_type::TYPE_VOID);
+ g_type_string = new t_base_type("string", t_base_type::TYPE_STRING);
+ g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING);
+ ((t_base_type*)g_type_binary)->set_binary(true);
+ g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING);
+ ((t_base_type*)g_type_slist)->set_string_list(true);
+ g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL);
+ g_type_i8 = new t_base_type("i8", t_base_type::TYPE_I8);
+ g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16);
+ g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32);
+ g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64);
+ g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE);
+}
+
+void clearGlobals() {
+ delete g_type_void;
+ delete g_type_string;
+ delete g_type_bool;
+ delete g_type_i8;
+ delete g_type_i16;
+ delete g_type_i32;
+ delete g_type_i64;
+ delete g_type_double;
+}
+
+/**
+ * The location of the last parsed doctext comment.
+ */
+int g_doctext_lineno;
+int g_program_doctext_lineno = 0;
+PROGDOCTEXT_STATUS g_program_doctext_status = INVALID;
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/common.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/common.h
new file mode 100644
index 000000000..694884636
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/common.h
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_COMMON_H
+#define T_COMMON_H
+
+#include "thrift/parse/t_type.h"
+
+/**
+ * Global types for the parser to be able to reference
+ */
+
+extern t_type* g_type_void;
+extern t_type* g_type_string;
+extern t_type* g_type_binary;
+extern t_type* g_type_slist;
+extern t_type* g_type_bool;
+extern t_type* g_type_i8;
+extern t_type* g_type_i16;
+extern t_type* g_type_i32;
+extern t_type* g_type_i64;
+extern t_type* g_type_double;
+
+void initGlobals();
+void clearGlobals();
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc
new file mode 100644
index 000000000..dd4d166ca
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc
@@ -0,0 +1,2592 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <sys/stat.h>
+#include <stdexcept>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * AS3 code generator.
+ *
+ */
+class t_as3_generator : public t_oop_generator {
+public:
+ t_as3_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ bindable_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("bindable") == 0) {
+ bindable_ = true;
+ } else {
+ throw "unknown option as3:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-as3";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ void print_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval = false);
+ std::string render_const_value(ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_as3_struct(t_struct* tstruct, bool is_exception);
+
+ void generate_as3_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool in_class = false,
+ bool is_result = false);
+ // removed -- equality,compare_to
+ void generate_as3_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_as3_validator(std::ostream& out, t_struct* tstruct);
+ void generate_as3_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_as3_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_as3_struct_tostring(std::ostream& out, t_struct* tstruct, bool bindable);
+ void generate_as3_meta_data_map(std::ostream& out, t_struct* tstruct);
+ void generate_field_value_meta_data(std::ostream& out, t_type* type);
+ std::string get_as3_type_string(t_type* type);
+ void generate_reflection_setters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_reflection_getters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct);
+ void generate_generic_isset_method(std::ostream& out, t_struct* tstruct);
+ void generate_as3_bean_boilerplate(std::ostream& out, t_struct* tstruct, bool bindable);
+
+ void generate_function_helpers(t_function* tfunction);
+ std::string get_cap_name(std::string name);
+ std::string generate_isset_check(t_field* field);
+ std::string generate_isset_check(std::string field);
+ void generate_isset_set(ostream& out, t_field* field);
+ // removed std::string isset_field_id(t_field* field);
+
+ void generate_service_interface(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string iter,
+ std::string map);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_as3_doc(std::ostream& out, t_doc* tdoc);
+
+ void generate_as3_doc(std::ostream& out, t_function* tdoc);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string as3_package();
+ std::string as3_type_imports();
+ std::string as3_thrift_imports();
+ std::string as3_thrift_gen_imports(t_struct* tstruct, string& imports);
+ std::string as3_thrift_gen_imports(t_service* tservice);
+ std::string type_name(t_type* ttype, bool in_container = false, bool in_init = false);
+ std::string base_type_name(t_base_type* tbase, bool in_container = false);
+ std::string declare_field(t_field* tfield, bool init = false);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string get_enum_class_name(t_type* type) override;
+
+ bool type_can_be_null(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception()
+ || ttype->is_string();
+ }
+
+ std::string constant_name(std::string name);
+
+private:
+ /**
+ * File streams
+ */
+
+ std::string package_name_;
+ ofstream_with_content_based_conditional_update f_service_;
+ std::string package_dir_;
+
+ bool bindable_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_as3_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+ package_name_ = program_->get_namespace("as3");
+
+ string dir = package_name_;
+ string subdir = get_out_dir();
+ string::size_type loc;
+ while ((loc = dir.find(".")) != string::npos) {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0) {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ package_dir_ = subdir;
+}
+
+/**
+ * Packages the generated file
+ *
+ * @return String of the package, i.e. "package org.apache.thriftdemo;"
+ */
+string t_as3_generator::as3_package() {
+ if (!package_name_.empty()) {
+ return string("package ") + package_name_ + " ";
+ }
+ return "package ";
+}
+
+/**
+ * Prints standard as3 imports
+ *
+ * @return List of imports for As3 types that are used in here
+ */
+string t_as3_generator::as3_type_imports() {
+ return string() + "import org.apache.thrift.Set;\n" + "import flash.utils.ByteArray;\n"
+ + "import flash.utils.Dictionary;\n\n";
+}
+
+/**
+ * Prints standard as3 imports
+ *
+ * @return List of imports necessary for thrift
+ */
+string t_as3_generator::as3_thrift_imports() {
+ return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n"
+ + "import org.apache.thrift.protocol.*;\n\n";
+}
+
+/**
+ * Prints imports needed for a given type
+ *
+ * @return List of imports necessary for a given t_struct
+ */
+string t_as3_generator::as3_thrift_gen_imports(t_struct* tstruct, string& imports) {
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // For each type check if it is from a differnet namespace
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_program* program = (*m_iter)->get_type()->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("as3");
+ if (!package.empty()) {
+ if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) {
+ imports.append("import " + package + "." + (*m_iter)->get_type()->get_name() + ";\n");
+ }
+ }
+ }
+ }
+ return imports;
+}
+
+/**
+ * Prints imports needed for a given type
+ *
+ * @return List of imports necessary for a given t_service
+ */
+string t_as3_generator::as3_thrift_gen_imports(t_service* tservice) {
+ string imports;
+ const vector<t_function*>& functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+
+ // For each type check if it is from a differnet namespace
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_program* program = (*f_iter)->get_returntype()->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("as3");
+ if (!package.empty()) {
+ if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) {
+ imports.append("import " + package + "." + (*f_iter)->get_returntype()->get_name()
+ + ";\n");
+ }
+ }
+ }
+
+ as3_thrift_gen_imports((*f_iter)->get_arglist(), imports);
+ as3_thrift_gen_imports((*f_iter)->get_xceptions(), imports);
+ }
+
+ return imports;
+}
+
+/**
+ * Nothing in As3
+ */
+void t_as3_generator::close_generator() {
+}
+
+/**
+ * Generates a typedef. This is not done in As3, since it does
+ * not support arbitrary name replacements, and it'd be a wacky waste
+ * of overhead to make wrapper classes.
+ *
+ * @param ttypedef The type definition
+ */
+void t_as3_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Enums are a class with a set of static constants.
+ *
+ * @param tenum The enumeration
+ */
+void t_as3_generator::generate_enum(t_enum* tenum) {
+ // Make output file
+ string f_enum_name = package_dir_ + "/" + (tenum->get_name()) + ".as";
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name);
+
+ // Comment and package it
+ f_enum << autogen_comment() << as3_package() << endl;
+
+ scope_up(f_enum);
+ // Add as3 imports
+ f_enum << string() + "import org.apache.thrift.Set;" << endl << "import flash.utils.Dictionary;"
+ << endl;
+
+ indent(f_enum) << "public class " << tenum->get_name() << " ";
+ scope_up(f_enum);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << "public static const " << (*c_iter)->get_name() << ":int = " << value << ";"
+ << endl;
+ }
+
+ // Create a static Set with all valid values for this enum
+ f_enum << endl;
+
+ indent(f_enum) << "public static const VALID_VALUES:Set = new Set(";
+ indent_up();
+ bool firstValue = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ // populate set
+ f_enum << (firstValue ? "" : ", ") << (*c_iter)->get_name();
+ firstValue = false;
+ }
+ indent_down();
+ f_enum << ");" << endl;
+
+ indent(f_enum) << "public static const VALUES_TO_NAMES:Dictionary = new Dictionary();" << endl;
+
+ scope_up(f_enum);
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ indent(f_enum) << "VALUES_TO_NAMES[" << (*c_iter)->get_name() << "] = \""
+ << (*c_iter)->get_name() << "\";" << endl;
+ }
+ f_enum << endl;
+
+ scope_down(f_enum);
+
+ scope_down(f_enum); // end class
+
+ scope_down(f_enum); // end package
+
+ f_enum.close();
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_as3_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+
+ string f_consts_name = package_dir_ + "/" + program_name_ + "Constants.as";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name);
+
+ // Print header
+ f_consts << autogen_comment() << as3_package();
+
+ scope_up(f_consts);
+ f_consts << endl;
+
+ f_consts << as3_type_imports();
+
+ indent(f_consts) << "public class " << program_name_ << "Constants {" << endl << endl;
+ indent_up();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ print_const_value(f_consts,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ false);
+ }
+ indent_down();
+ indent(f_consts) << "}" << endl;
+ scope_down(f_consts);
+ f_consts.close();
+}
+
+void t_as3_generator::print_const_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval) {
+ type = get_true_type(type);
+
+ indent(out);
+ if (!defval) {
+ out << (in_static ? "var " : "public static const ");
+ }
+ if (type->is_base_type()) {
+ string v2 = render_const_value(out, name, type, value);
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = " << v2 << ";" << endl << endl;
+ } else if (type->is_enum()) {
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = " << value->get_integer() << ";" << endl << endl;
+ } else if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ out << name << ":" << type_name(type) << " = new " << type_name(type, false, true) << "();"
+ << endl;
+ if (!in_static) {
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "new function():void {" << endl;
+ indent_up();
+ }
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ indent(out) << name << ".";
+ out << v_iter->first->get_string() << " = " << val << ";" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_map()) {
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "new function():void {" << endl;
+ indent_up();
+ }
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ indent(out) << name << "[" << key << "] = " << val << ";" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_list() || type->is_set()) {
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "new function():void {" << endl;
+ indent_up();
+ }
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ indent(out) << name << "." << (type->is_list() ? "push" : "add") << "(" << val << ");"
+ << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else {
+ throw "compiler error: no const of type " + type->get_name();
+ }
+}
+
+string t_as3_generator::render_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+ type = get_true_type(type);
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ render << "(byte)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I16:
+ render << "(short)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ render << value->get_integer() << "L";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << "(double)" << value->get_integer();
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << value->get_integer();
+ } else {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is a class
+ * with data members, read(), write(), and an inner Isset class.
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_struct(t_struct* tstruct) {
+ generate_as3_struct(tstruct, false);
+}
+
+/**
+ * Exceptions are structs, but they inherit from Exception
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_xception(t_struct* txception) {
+ generate_as3_struct(txception, true);
+}
+
+/**
+ * As3 struct definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_struct(t_struct* tstruct, bool is_exception) {
+ // Make output file
+ string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".as";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << as3_package();
+
+ scope_up(f_struct);
+ f_struct << endl;
+
+ string imports;
+
+ f_struct << as3_type_imports() << as3_thrift_imports() << as3_thrift_gen_imports(tstruct, imports)
+ << endl;
+
+ if (bindable_ && !is_exception) {
+ f_struct << "import flash.events.Event;" << endl << "import flash.events.EventDispatcher;"
+ << endl << "import mx.events.PropertyChangeEvent;" << endl;
+ }
+
+ generate_as3_struct_definition(f_struct, tstruct, is_exception);
+
+ scope_down(f_struct); // end of package
+ f_struct.close();
+}
+
+/**
+ * As3 struct definition. This has various parameters, as it could be
+ * generated standalone or inside another class as a helper. If it
+ * is a helper than it is a static class.
+ *
+ * @param tstruct The struct definition
+ * @param is_exception Is this an exception?
+ * @param in_class If inside a class, needs to be static class
+ * @param is_result If this is a result it needs a different writer
+ */
+void t_as3_generator::generate_as3_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool in_class,
+ bool is_result) {
+ generate_as3_doc(out, tstruct);
+
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+ bool bindable = !is_exception && !in_class && bindable_;
+
+ indent(out) << (in_class ? "" : "public ") << (is_final ? "final " : "") << "class "
+ << tstruct->get_name() << " ";
+
+ if (is_exception) {
+ out << "extends Error ";
+ } else if (bindable) {
+ out << "extends EventDispatcher ";
+ }
+ out << "implements TBase ";
+
+ scope_up(out);
+
+ indent(out) << "private static const STRUCT_DESC:TStruct = new TStruct(\"" << tstruct->get_name()
+ << "\");" << endl;
+
+ // Members are public for -as3, private for -as3bean
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "private static const " << constant_name((*m_iter)->get_name())
+ << "_FIELD_DESC:TField = new TField(\"" << (*m_iter)->get_name() << "\", "
+ << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << ");"
+ << endl;
+ }
+
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_as3_doc(out, *m_iter);
+ indent(out) << "private var _" << (*m_iter)->get_name() + ":" + type_name((*m_iter)->get_type())
+ << ";" << endl;
+
+ indent(out) << "public static const " << upcase_string((*m_iter)->get_name())
+ << ":int = " << (*m_iter)->get_key() << ";" << endl;
+ }
+
+ out << endl;
+
+ // Inner Isset class
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!type_can_be_null((*m_iter)->get_type())) {
+ indent(out) << "private var __isset_" << (*m_iter)->get_name() << ":Boolean = false;"
+ << endl;
+ }
+ }
+ }
+
+ out << endl;
+
+ generate_as3_meta_data_map(out, tstruct);
+
+ // Static initializer to populate global class to struct metadata map
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "FieldMetaData.addStructMetaDataMap(" << type_name(tstruct) << ", metaDataMap);"
+ << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Default constructor
+ indent(out) << "public function " << tstruct->get_name() << "() {" << endl;
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_value() != NULL) {
+ indent(out) << "this._" << (*m_iter)->get_name() << " = "
+ << (*m_iter)->get_value()->get_integer() << ";" << endl;
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ generate_as3_bean_boilerplate(out, tstruct, bindable);
+ generate_generic_field_getters_setters(out, tstruct);
+ generate_generic_isset_method(out, tstruct);
+
+ generate_as3_struct_reader(out, tstruct);
+ if (is_result) {
+ generate_as3_struct_result_writer(out, tstruct);
+ } else {
+ generate_as3_struct_writer(out, tstruct);
+ }
+ generate_as3_struct_tostring(out, tstruct, bindable);
+ generate_as3_validator(out, tstruct);
+ scope_down(out);
+ out << endl;
+}
+
+/**
+ * Generates a function to read all the fields of the struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_struct_reader(ostream& out, t_struct* tstruct) {
+ out << indent() << "public function read(iprot:TProtocol):void {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Declare stack tmp variables and read struct header
+ out << indent() << "var field:TField;" << endl << indent() << "iprot.readStructBegin();" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true)" << endl;
+ scope_up(out);
+
+ // Read beginning field marker
+ indent(out) << "field = iprot.readFieldBegin();" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if (field.type == TType.STOP) { " << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Switch statement on the field we are reading
+ indent(out) << "switch (field.id)" << endl;
+
+ scope_up(out);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << upcase_string((*f_iter)->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter, "this.");
+ generate_isset_set(out, *f_iter);
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " TProtocolUtil.skip(iprot, field.type);"
+ << endl << indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ out << indent() << "default:" << endl << indent() << " TProtocolUtil.skip(iprot, field.type);"
+ << endl << indent() << " break;" << endl;
+
+ scope_down(out);
+
+ // Read field end marker
+ indent(out) << "iprot.readFieldEnd();" << endl;
+
+ scope_down(out);
+
+ out << indent() << "iprot.readStructEnd();" << endl << endl;
+
+ // in non-beans style, check for required fields of primitive type
+ // (which can be checked here but not in the general validate method)
+ out << endl << indent() << "// check for required fields of primitive type, which can't be "
+ "checked in the validate method" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) {
+ out << indent() << "if (!__isset_" << (*f_iter)->get_name() << ") {" << endl << indent()
+ << " throw new TProtocolError(TProtocolError.UNKNOWN, \"Required field '"
+ << (*f_iter)->get_name()
+ << "' was not found in serialized data! Struct: \" + toString());" << endl << indent()
+ << "}" << endl;
+ }
+ }
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+// generates as3 method to perform various checks
+// (e.g. check that all required fields are set)
+void t_as3_generator::generate_as3_validator(ostream& out, t_struct* tstruct) {
+ indent(out) << "public function validate():void {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "// check for required fields" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ if (type_can_be_null((*f_iter)->get_type())) {
+ indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out) << " throw new TProtocolError(TProtocolError.UNKNOWN, \"Required field '"
+ << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());"
+ << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name()
+ << "' because it's a primitive and you chose the non-beans generator." << endl;
+ }
+ }
+ }
+
+ // check that fields of type enum have valid values
+ out << indent() << "// check that fields of type enum have valid values" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ t_type* type = field->get_type();
+ // if field is an enum, check that its value is valid
+ if (type->is_enum()) {
+ indent(out) << "if (" << generate_isset_check(field) << " && !" << get_enum_class_name(type)
+ << ".VALID_VALUES.contains(" << field->get_name() << ")){" << endl;
+ indent_up();
+ indent(out) << "throw new TProtocolError(TProtocolError.UNKNOWN, \"The field '"
+ << field->get_name() << "' has been assigned the invalid value \" + "
+ << field->get_name() << ");" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_struct_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public function write(oprot:TProtocol):void {" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl << endl;
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ }
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl;
+ indent_up();
+ }
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ if (null_allowed) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ if (could_be_unset) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+ // Write the struct map
+ out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();"
+ << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct,
+ * which is a function result. These fields are only written
+ * if they are set in the Isset array, and only one of them
+ * can be set at a time.
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_struct_result_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public function write(oprot:TProtocol):void {" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ out << endl << indent() << "if ";
+ } else {
+ out << " else if ";
+ }
+
+ out << "(this." << generate_isset_check(*f_iter) << ") {" << endl;
+
+ indent_up();
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}";
+ }
+ // Write the struct map
+ out << endl << indent() << "oprot.writeFieldStop();" << endl << indent()
+ << "oprot.writeStructEnd();" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_as3_generator::generate_reflection_getters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ (void)type;
+ (void)cap_name;
+ indent(out) << "case " << upcase_string(field_name) << ":" << endl;
+ indent_up();
+ indent(out) << "return this." << field_name << ";" << endl;
+ indent_down();
+}
+
+void t_as3_generator::generate_reflection_setters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ (void)type;
+ (void)cap_name;
+ indent(out) << "case " << upcase_string(field_name) << ":" << endl;
+ indent_up();
+ indent(out) << "if (value == null) {" << endl;
+ indent(out) << " unset" << get_cap_name(field_name) << "();" << endl;
+ indent(out) << "} else {" << endl;
+ indent(out) << " this." << field_name << " = value;" << endl;
+ indent(out) << "}" << endl;
+ indent(out) << "break;" << endl << endl;
+
+ indent_down();
+}
+
+void t_as3_generator::generate_generic_field_getters_setters(std::ostream& out,
+ t_struct* tstruct) {
+
+ std::ostringstream getter_stream;
+ std::ostringstream setter_stream;
+
+ // build up the bodies of both the getter and setter at once
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ indent_up();
+ generate_reflection_setters(setter_stream, type, field_name, cap_name);
+ generate_reflection_getters(getter_stream, type, field_name, cap_name);
+ indent_down();
+ }
+
+ // create the setter
+ indent(out) << "public function setFieldValue(fieldID:int, value:*):void {" << endl;
+ indent_up();
+
+ indent(out) << "switch (fieldID) {" << endl;
+
+ out << setter_stream.str();
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // create the getter
+ indent(out) << "public function getFieldValue(fieldID:int):* {" << endl;
+ indent_up();
+
+ indent(out) << "switch (fieldID) {" << endl;
+
+ out << getter_stream.str();
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+
+ indent(out) << "}" << endl;
+
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+// Creates a generic isSet method that takes the field number as argument
+void t_as3_generator::generate_generic_isset_method(std::ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // create the isSet method
+ indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a "
+ "value) and false otherwise" << endl;
+ indent(out) << "public function isSet(fieldID:int):Boolean {" << endl;
+ indent_up();
+ indent(out) << "switch (fieldID) {" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ indent(out) << "case " << upcase_string(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << "return " << generate_isset_check(field) << ";" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a set of As3 Bean boilerplate functions (setters, getters, etc.)
+ * for the given struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_bean_boilerplate(ostream& out,
+ t_struct* tstruct,
+ bool bindable) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ // Simple getter
+ generate_as3_doc(out, field);
+ indent(out) << "public function get " << field_name << "():" << type_name(type) << " {" << endl;
+ indent_up();
+ indent(out) << "return this._" << field_name << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Simple setter
+ generate_as3_doc(out, field);
+ std::string propName = tmp("thriftPropertyChange");
+ if (bindable) {
+ indent(out) << "[Bindable(event=\"" << propName << "\")]" << endl;
+ }
+ indent(out) << "public function set " << field_name << "(" << field_name << ":"
+ << type_name(type) << "):void {" << endl;
+ indent_up();
+ indent(out) << "this._" << field_name << " = " << field_name << ";" << endl;
+ generate_isset_set(out, field);
+
+ if (bindable) {
+ // We have to use a custom event rather than the default, because if you use the default,
+ // the setter only gets called if the value has changed - this means calling
+ // foo.setIntValue(0)
+ // will not cause foo.isIntValueSet() to return true since the value of foo._intValue wasn't
+ // changed
+ // so the setter was never called.
+ indent(out) << "dispatchEvent(new Event(\"" << propName << "\"));" << endl;
+
+ // However, if you just use a custom event, then collections won't be able to detect when
+ // elements
+ // in the collections have changed since they listed for PropertyChangeEvents. So, we
+ // dispatch both.
+ indent(out) << "dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE));"
+ << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Unsetter
+ indent(out) << "public function unset" << cap_name << "():void {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "this." << field_name << " = null;" << endl;
+ } else {
+ indent(out) << "this.__isset_" << field_name << " = false;" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // isSet method
+ indent(out) << "// Returns true if field " << field_name
+ << " is set (has been assigned a value) and false otherwise" << endl;
+ indent(out) << "public function is" << get_cap_name("set") << cap_name << "():Boolean {"
+ << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "return this." << field_name << " != null;" << endl;
+ } else {
+ indent(out) << "return this.__isset_" << field_name << ";" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a toString() method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_struct_tostring(ostream& out,
+ t_struct* tstruct,
+ bool bindable) {
+ // If it's bindable, it extends EventDispatcher so toString is an override.
+ out << indent() << "public " << (bindable ? "override " : "") << "function toString():String {"
+ << endl;
+ indent_up();
+
+ out << indent() << "var ret:String = new String(\"" << tstruct->get_name() << "(\");" << endl;
+ out << indent() << "var first:Boolean = true;" << endl << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ }
+
+ t_field* field = (*f_iter);
+
+ if (!first) {
+ indent(out) << "if (!first) ret += \", \";" << endl;
+ }
+ indent(out) << "ret += \"" << (*f_iter)->get_name() << ":\";" << endl;
+ bool can_be_null = type_can_be_null(field->get_type());
+ if (can_be_null) {
+ indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out) << " ret += \"null\";" << endl;
+ indent(out) << "} else {" << endl;
+ indent_up();
+ }
+
+ if (field->get_type()->is_binary()) {
+ indent(out) << " ret += \"BINARY\";" << endl;
+ } else if (field->get_type()->is_enum()) {
+ indent(out) << "var " << field->get_name()
+ << "_name:String = " << get_enum_class_name(field->get_type())
+ << ".VALUES_TO_NAMES[this." << (*f_iter)->get_name() << "];" << endl;
+ indent(out) << "if (" << field->get_name() << "_name != null) {" << endl;
+ indent(out) << " ret += " << field->get_name() << "_name;" << endl;
+ indent(out) << " ret += \" (\";" << endl;
+ indent(out) << "}" << endl;
+ indent(out) << "ret += this." << field->get_name() << ";" << endl;
+ indent(out) << "if (" << field->get_name() << "_name != null) {" << endl;
+ indent(out) << " ret += \")\";" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "ret += this." << (*f_iter)->get_name() << ";" << endl;
+ }
+
+ if (can_be_null) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ indent(out) << "first = false;" << endl;
+
+ if (could_be_unset) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ first = false;
+ }
+ out << indent() << "ret += \")\";" << endl << indent() << "return ret;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a static map with meta data to store information such as fieldID to
+ * fieldName mapping
+ *
+ * @param tstruct The struct definition
+ */
+void t_as3_generator::generate_as3_meta_data_map(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Static Map with fieldID -> FieldMetaData mappings
+ indent(out) << "public static const metaDataMap:Dictionary = new Dictionary();" << endl;
+
+ if (fields.size() > 0) {
+ // Populate map
+ scope_up(out);
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ std::string field_name = field->get_name();
+ indent(out) << "metaDataMap[" << upcase_string(field_name) << "] = new FieldMetaData(\""
+ << field_name << "\", ";
+
+ // Set field requirement type (required, optional, etc.)
+ if (field->get_req() == t_field::T_REQUIRED) {
+ out << "TFieldRequirementType.REQUIRED, ";
+ } else if (field->get_req() == t_field::T_OPTIONAL) {
+ out << "TFieldRequirementType.OPTIONAL, ";
+ } else {
+ out << "TFieldRequirementType.DEFAULT, ";
+ }
+
+ // Create value meta data
+ generate_field_value_meta_data(out, field->get_type());
+ out << ");" << endl;
+ }
+ scope_down(out);
+ }
+}
+
+/**
+ * Returns a string with the as3 representation of the given thrift type
+ * (e.g. for the type struct it returns "TType.STRUCT")
+ */
+std::string t_as3_generator::get_as3_type_string(t_type* type) {
+ if (type->is_list()) {
+ return "TType.LIST";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_typedef()) {
+ return get_as3_type_string(((t_typedef*)type)->get_type());
+ } else if (type->is_base_type()) {
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "TType.VOID";
+ break;
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ break;
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ break;
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ break;
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ break;
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ break;
+ default:
+ throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_as3_generator::get_as3_type_string!");
+ break; // This should never happen!
+ }
+ } else {
+ throw std::runtime_error(
+ "Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_as3_generator::get_as3_type_string!"); // This should never happen!
+ }
+}
+
+void t_as3_generator::generate_field_value_meta_data(std::ostream& out, t_type* type) {
+ out << endl;
+ indent_up();
+ indent_up();
+ if (type->is_struct() || type->is_xception()) {
+ indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type);
+ } else if (type->is_container()) {
+ if (type->is_list()) {
+ indent(out) << "new ListMetaData(TType.LIST, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else if (type->is_set()) {
+ indent(out) << "new SetMetaData(TType.SET, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else { // map
+ indent(out) << "new MapMetaData(TType.MAP, ";
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+ generate_field_value_meta_data(out, key_type);
+ out << ", ";
+ generate_field_value_meta_data(out, val_type);
+ }
+ } else {
+ indent(out) << "new FieldValueMetaData(" << get_as3_type_string(type);
+ }
+ out << ")";
+ indent_down();
+ indent_down();
+}
+
+/**
+ * Generates a thrift service. In C++, this comprises an entirely separate
+ * header and source file. The header file defines the methods and includes
+ * the data types defined in the main header file, and the implementation
+ * file contains implementations of the basic printer and default interfaces.
+ *
+ * @param tservice The service definition
+ */
+void t_as3_generator::generate_service(t_service* tservice) {
+ // Make interface file
+ string f_service_name = package_dir_ + "/" + service_name_ + ".as";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << as3_package();
+
+ scope_up(f_service_);
+
+ f_service_ << endl << as3_type_imports() << as3_thrift_imports()
+ << as3_thrift_gen_imports(tservice);
+
+ if (tservice->get_extends() != NULL) {
+ t_type* parent = tservice->get_extends();
+ string parent_namespace = parent->get_program()->get_namespace("as3");
+ if (!parent_namespace.empty() && parent_namespace != package_name_) {
+ f_service_ << "import " << type_name(parent) << ";" << endl;
+ }
+ }
+
+ f_service_ << endl;
+
+ generate_service_interface(tservice);
+
+ scope_down(f_service_);
+ f_service_.close();
+
+ // Now make the implementation/client file
+ f_service_name = package_dir_ + "/" + service_name_ + "Impl.as";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << as3_package();
+
+ scope_up(f_service_);
+
+ f_service_ << endl << as3_type_imports() << as3_thrift_imports()
+ << as3_thrift_gen_imports(tservice);
+
+ if (tservice->get_extends() != NULL) {
+ t_type* parent = tservice->get_extends();
+ string parent_namespace = parent->get_program()->get_namespace("as3");
+ if (!parent_namespace.empty() && parent_namespace != package_name_) {
+ f_service_ << "import " << type_name(parent) << "Impl;" << endl;
+ }
+ }
+
+ f_service_ << endl;
+
+ generate_service_client(tservice);
+ scope_down(f_service_);
+
+ f_service_ << as3_type_imports();
+ f_service_ << as3_thrift_imports();
+ f_service_ << as3_thrift_gen_imports(tservice);
+ if (!package_name_.empty()) {
+ f_service_ << "import " << package_name_ << ".*;" << endl;
+ }
+
+ generate_service_helpers(tservice);
+
+ f_service_.close();
+
+ // Now make the processor/server file
+ f_service_name = package_dir_ + "/" + service_name_ + "Processor.as";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << as3_package();
+
+ scope_up(f_service_);
+
+ f_service_ << endl << as3_type_imports() << as3_thrift_imports()
+ << as3_thrift_gen_imports(tservice) << endl;
+
+ generate_service_server(tservice);
+ scope_down(f_service_);
+
+ f_service_ << as3_type_imports();
+ f_service_ << as3_thrift_imports();
+ f_service_ << as3_thrift_gen_imports(tservice) << endl;
+ if (!package_name_.empty()) {
+ f_service_ << "import " << package_name_ << ".*;" << endl;
+ }
+
+ generate_service_helpers(tservice);
+
+ f_service_.close();
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_as3_generator::generate_service_interface(t_service* tservice) {
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends_iface = " extends " + tservice->get_extends()->get_name();
+ }
+
+ generate_as3_doc(f_service_, tservice);
+ f_service_ << indent() << "public interface " << service_name_ << extends_iface << " {" << endl
+ << endl;
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_as3_doc(f_service_, *f_iter);
+ if (!(*f_iter)->is_oneway()) {
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "//function onError(Error):void;" << endl;
+ indent(f_service_) << "//function onSuccess():void;" << endl;
+ } else {
+ indent(f_service_) << "//function onError(Error):void;" << endl;
+ indent(f_service_) << "//function onSuccess(" << type_name((*f_iter)->get_returntype())
+ << "):void;" << endl;
+ }
+ }
+ indent(f_service_) << function_signature(*f_iter) << ";" << endl << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates structs for all the service args and return types
+ *
+ * @param tservice The service
+ */
+void t_as3_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_as3_struct_definition(f_service_, ts, false, true);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_as3_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = tservice->get_extends()->get_name();
+ extends_client = " extends " + extends + "Impl";
+ }
+
+ indent(f_service_) << "public class " << service_name_ << "Impl" << extends_client
+ << " implements " << service_name_ << " {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public function " << service_name_ << "Impl"
+ << "(iprot:TProtocol, oprot:TProtocol=null)" << endl;
+ scope_up(f_service_);
+ if (extends.empty()) {
+ f_service_ << indent() << "iprot_ = iprot;" << endl;
+ f_service_ << indent() << "if (oprot == null) {" << endl;
+ indent_up();
+ f_service_ << indent() << "oprot_ = iprot;" << endl;
+ indent_down();
+ f_service_ << indent() << "} else {" << endl;
+ indent_up();
+ f_service_ << indent() << "oprot_ = oprot;" << endl;
+ indent_down();
+ f_service_ << indent() << "}";
+ } else {
+ f_service_ << indent() << "super(iprot, oprot);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected var iprot_:TProtocol;" << endl << indent()
+ << "protected var oprot_:TProtocol;" << endl << endl << indent()
+ << "protected var seqid_:int;" << endl << endl;
+
+ indent(f_service_) << "public function getInputProtocol():TProtocol" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return this.iprot_;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << "public function getOutputProtocol():TProtocol" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return this.oprot_;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ if (!(*f_iter)->is_oneway()) {
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "//function onError(Error):void;" << endl;
+ indent(f_service_) << "//function onSuccess():void;" << endl;
+ } else {
+ indent(f_service_) << "//function onError(Error):void;" << endl;
+ indent(f_service_) << "//function onSuccess(" << type_name((*f_iter)->get_returntype())
+ << "):void;" << endl;
+ }
+ }
+ indent(f_service_) << "public " << function_signature(*f_iter) << endl;
+ scope_up(f_service_);
+
+ // Get the struct of function call params
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ string argsname = (*f_iter)->get_name() + "_args";
+ vector<t_field*>::const_iterator fld_iter;
+ const vector<t_field*>& fields = arg_struct->get_members();
+
+ // Serialize the request
+ f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", "
+ << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL")
+ << ", seqid_));" << endl << indent() << "var args:" << argsname << " = new "
+ << argsname << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = "
+ << (*fld_iter)->get_name() << ";" << endl;
+ }
+
+ f_service_ << indent() << "args.write(oprot_);" << endl << indent()
+ << "oprot_.writeMessageEnd();" << endl;
+
+ if ((*f_iter)->is_oneway()) {
+ f_service_ << indent() << "oprot_.getTransport().flush();" << endl;
+ } else {
+ f_service_ << indent() << "oprot_.getTransport().flush(function(error:Error):void {" << endl;
+ indent_up();
+ f_service_ << indent() << "try {" << endl;
+ indent_up();
+ string resultname = (*f_iter)->get_name() + "_result";
+ f_service_ << indent() << "if (error != null) {" << endl << indent()
+ << " if (onError != null) onError(error);" << endl << indent() << " return;"
+ << endl << indent() << "}" << endl << indent()
+ << "var msg:TMessage = iprot_.readMessageBegin();" << endl << indent()
+ << "if (msg.type == TMessageType.EXCEPTION) {" << endl << indent()
+ << " var x:TApplicationError = TApplicationError.read(iprot_);" << endl
+ << indent() << " iprot_.readMessageEnd();" << endl << indent()
+ << " if (onError != null) onError(x);" << endl << indent() << " return;" << endl
+ << indent() << "}" << endl << indent() << "var result :" << resultname << " = new "
+ << resultname << "();" << endl << indent() << "result.read(iprot_);" << endl
+ << indent() << "iprot_.readMessageEnd();" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl
+ << indent() << " if (onSuccess != null) onSuccess(result.success);" << endl
+ << indent() << " return;" << endl << indent() << "}" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl
+ << indent() << " if (onError != null) onError(result." << (*x_iter)->get_name()
+ << ");" << endl << indent() << " return;" << endl << indent() << "}" << endl;
+ }
+
+ // If you get here it's an exception, unless a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if (onSuccess != null) onSuccess();" << endl << indent()
+ << "return;" << endl;
+ } else {
+
+ f_service_ << indent() << "if (onError != null) onError(new "
+ "TApplicationError(TApplicationError.MISSING_RESULT, \""
+ << (*f_iter)->get_name() << " failed: unknown result\"));" << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "} catch (e:TError) {" << endl << indent()
+ << " if (onError != null) onError(e);" << endl << indent() << "}" << endl;
+
+ indent_down();
+ indent(f_service_) << "});" << endl;
+ }
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_as3_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Extends stuff
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_processor = " extends " + extends + "Processor";
+ }
+
+ // Generate the header portion
+ indent(f_service_) << "public class " << service_name_ << "Processor" << extends_processor
+ << " implements TProcessor {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public function " << service_name_ << "Processor(iface:" << service_name_
+ << ")" << endl;
+ scope_up(f_service_);
+ if (!extends.empty()) {
+ f_service_ << indent() << "super(iface);" << endl;
+ }
+ f_service_ << indent() << "iface_ = iface;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "PROCESS_MAP[\"" << (*f_iter)->get_name()
+ << "\"] = " << (*f_iter)->get_name() << "();" << endl;
+ }
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ f_service_ << indent() << "private var iface_:" << service_name_ << ";" << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected const PROCESS_MAP:Dictionary = new Dictionary();" << endl;
+ }
+
+ f_service_ << endl;
+
+ // Generate the server implementation
+ string override = "";
+ if (tservice->get_extends() != NULL) {
+ override = "override ";
+ }
+ indent(f_service_) << override
+ << "public function process(iprot:TProtocol, oprot:TProtocol):Boolean" << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "var msg:TMessage = iprot.readMessageBegin();" << endl;
+
+ // TODO(mcslee): validate message, was the seqid etc. legit?
+ // AS- If all method is oneway:
+ // do you have an oprot?
+ // do you you need nullcheck?
+ f_service_
+ << indent() << "var fn:Function = PROCESS_MAP[msg.name];" << endl << indent()
+ << "if (fn == null) {" << endl << indent() << " TProtocolUtil.skip(iprot, TType.STRUCT);"
+ << endl << indent() << " iprot.readMessageEnd();" << endl << indent()
+ << " var x:TApplicationError = new TApplicationError(TApplicationError.UNKNOWN_METHOD, "
+ "\"Invalid method name: '\"+msg.name+\"'\");" << endl << indent()
+ << " oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));"
+ << endl << indent() << " x.write(oprot);" << endl << indent() << " oprot.writeMessageEnd();"
+ << endl << indent() << " oprot.getTransport().flush();" << endl << indent()
+ << " return true;" << endl << indent() << "}" << endl << indent()
+ << "fn.call(this,msg.seqid, iprot, oprot);" << endl;
+
+ f_service_ << indent() << "return true;" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_as3_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_as3_struct_definition(f_service_, &result, false, true, true);
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_as3_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open class
+ indent(f_service_) << "private function " << tfunction->get_name() << "():Function {" << endl;
+ indent_up();
+
+ // Open function
+ indent(f_service_) << "return function(seqid:int, iprot:TProtocol, oprot:TProtocol):void" << endl;
+ scope_up(f_service_);
+
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+
+ f_service_ << indent() << "var args:" << argsname << " = new " << argsname << "();" << endl
+ << indent() << "args.read(iprot);" << endl << indent() << "iprot.readMessageEnd();"
+ << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "var result:" << resultname << " = new " << resultname << "();"
+ << endl;
+ }
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "try {" << endl;
+ indent_up();
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (tfunction->is_oneway()) {
+ f_service_ << "iface_." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+ } else {
+ f_service_ << "// sorry this operation is not supported yet" << endl;
+ f_service_ << indent() << "throw new Error(\"This is not yet supported\");" << endl;
+ }
+
+ // Set isset on success field
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()
+ && !type_can_be_null(tfunction->get_returntype())) {
+ f_service_ << indent() << "result.set" << get_cap_name("success") << get_cap_name("isSet")
+ << "(true);" << endl;
+ }
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ indent_down();
+ f_service_ << indent() << "}";
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << " catch (" << (*x_iter)->get_name() << ":"
+ << type_name((*x_iter)->get_type(), false, false) << ") {" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ f_service_ << indent() << "result." << (*x_iter)->get_name() << " = "
+ << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ f_service_ << indent() << "}";
+ } else {
+ f_service_ << "}";
+ }
+ }
+ f_service_ << " catch (th:Error) {" << endl;
+ indent_up();
+ f_service_ << indent() << "trace(\"Internal error processing " << tfunction->get_name()
+ << "\", th);" << endl << indent()
+ << "var x:TApplicationError = new "
+ "TApplicationError(TApplicationError.INTERNAL_ERROR, \"Internal error processing "
+ << tfunction->get_name() << "\");" << endl << indent()
+ << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.EXCEPTION, seqid));" << endl << indent() << "x.write(oprot);"
+ << endl << indent() << "oprot.writeMessageEnd();" << endl << indent()
+ << "oprot.getTransport().flush();" << endl << indent() << "return;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "return;" << endl;
+ scope_down(f_service_);
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+ return;
+ }
+
+ f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);"
+ << endl << indent() << "oprot.writeMessageEnd();" << endl << indent()
+ << "oprot.getTransport().flush();" << endl;
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ *
+ * @param tfield The field
+ * @param prefix The variable name or container for this field
+ */
+void t_as3_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent(out) << name << " = iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "readBinary();";
+ } else {
+ out << "readString();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble();";
+ break;
+ default:
+ throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32();";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, invokes read()
+ */
+void t_as3_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent()
+ << prefix << ".read(iprot);" << endl;
+}
+
+/**
+ * Deserializes a container by reading its size and then iterating
+ */
+void t_as3_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "var " << obj << ":TMap = iprot.readMapBegin();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "var " << obj << ":TSet = iprot.readSetBegin();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "var " << obj << ":TList = iprot.readListBegin();" << endl;
+ }
+
+ indent(out) << prefix << " = new " << type_name(ttype, false, true)
+ // size the collection correctly
+ << "("
+ << ");" << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (var " << i << ":int = 0; " << i << " < " << obj << ".size"
+ << "; "
+ << "++" << i << ")" << endl;
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot.readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot.readListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_as3_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey) << endl;
+ indent(out) << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+/**
+ * Deserializes a set element
+ */
+void t_as3_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Deserializes a list element
+ */
+void t_as3_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".push(" << elem << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_as3_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name());
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + tfield->get_name();
+ indent(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ");";
+ } else {
+ out << "writeString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_as3_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ out << indent() << prefix << ".write(oprot);" << endl;
+}
+
+/**
+ * Serializes a container by writing its size then the elements.
+ *
+ * @param ttype The type of container
+ * @param prefix String prefix for fields
+ */
+void t_as3_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ string iter = tmp("_key");
+ string counter = tmp("_sizeCounter");
+ indent(out) << "var " << counter << ":int = 0;" << endl;
+ indent(out) << "for (var " << iter << ":* in " << prefix << ") {" << endl;
+ indent(out) << " " << counter << +"++;" << endl;
+ indent(out) << "}" << endl;
+
+ indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << counter << "));"
+ << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", " << prefix << ".size));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListBegin(new TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));"
+ << endl;
+ }
+
+ string iter = tmp("elem");
+ if (ttype->is_map()) {
+ indent(out) << "for (var " << iter << ":* in " << prefix << ")";
+ } else if (ttype->is_set()) {
+ indent(out) << "for each (var " << iter << ":* in " << prefix << ".toArray())";
+ } else if (ttype->is_list()) {
+ indent(out) << "for each (var " << iter << ":* in " << prefix << ")";
+ }
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter, prefix);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ }
+
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ */
+void t_as3_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string iter,
+ string map) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "");
+ t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_as3_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_as3_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Returns a As3 type name
+ *
+ * @param ttype The type
+ * @param container Is the type going inside a container?
+ * @return As3 type name, i.e. HashMap<Key,Value>
+ */
+string t_as3_generator::type_name(t_type* ttype, bool in_container, bool in_init) {
+ (void)in_init;
+ // In As3 typedefs are just resolved to their real type
+ ttype = get_true_type(ttype);
+ string prefix;
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype, in_container);
+ } else if (ttype->is_enum()) {
+ return "int";
+ } else if (ttype->is_map()) {
+ return "Dictionary";
+ } else if (ttype->is_set()) {
+ return "Set";
+ } else if (ttype->is_list()) {
+ return "Array";
+ }
+
+ // Check for namespacing
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("as3");
+ if (!package.empty()) {
+ return package + "." + ttype->get_name();
+ }
+ }
+
+ return ttype->get_name();
+}
+
+/**
+ * Returns the AS3 type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ * @param container Is it going in a As3 container?
+ */
+string t_as3_generator::base_type_name(t_base_type* type, bool in_container) {
+ (void)in_container;
+ t_base_type::t_base tbase = type->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ return "ByteArray";
+ } else {
+ return "String";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "Boolean";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ return "int";
+ case t_base_type::TYPE_I64:
+ throw "i64 is not yet supported in as3";
+ case t_base_type::TYPE_DOUBLE:
+ return "Number";
+ default:
+ throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_as3_generator::declare_field(t_field* tfield, bool init) {
+ // TODO(mcslee): do we ever need to initialize the field?
+ string result = "var " + tfield->get_name() + ":" + type_name(tfield->get_type());
+ if (init) {
+ t_type* ttype = get_true_type(tfield->get_type());
+ if (ttype->is_base_type() && tfield->get_value() != NULL) {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
+ } else if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+
+ } else if (ttype->is_enum()) {
+ result += " = 0";
+ } else if (ttype->is_container()) {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ } else {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ ;
+ }
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_as3_generator::function_signature(t_function* tfunction, string prefix) {
+ std::string arguments = argument_list(tfunction->get_arglist());
+ if (!tfunction->is_oneway()) {
+ if (arguments != "") {
+ arguments += ", ";
+ }
+ arguments += "onError:Function, onSuccess:Function";
+ }
+
+ std::string result = "function " + prefix + tfunction->get_name() + "(" + arguments + "):void";
+ return result;
+}
+
+/**
+ * Renders a comma separated field list, with type names
+ */
+string t_as3_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += (*f_iter)->get_name() + ":" + type_name((*f_iter)->get_type());
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_as3_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Applies the correct style to a string based on the value of nocamel_style_
+ */
+std::string t_as3_generator::get_cap_name(std::string name) {
+ name[0] = toupper(name[0]);
+ return name;
+}
+
+string t_as3_generator::constant_name(string name) {
+ string constant_name;
+
+ bool is_first = true;
+ bool was_previous_char_upper = false;
+ for (char character : name) {
+ bool is_upper = isupper(character);
+
+ if (is_upper && !is_first && !was_previous_char_upper) {
+ constant_name += '_';
+ }
+ constant_name += toupper(character);
+
+ is_first = false;
+ was_previous_char_upper = is_upper;
+ }
+
+ return constant_name;
+}
+
+/**
+ * Emits a As3Doc comment if the provided object has a doc in Thrift
+ */
+void t_as3_generator::generate_as3_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_docstring_comment(out, "/**\n", " * ", tdoc->get_doc(), " */\n");
+ }
+}
+
+/**
+ * Emits a As3Doc comment if the provided function object has a doc in Thrift
+ */
+void t_as3_generator::generate_as3_doc(ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc()) {
+ stringstream ss;
+ ss << tfunction->get_doc();
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ss << "\n@param " << p->get_name();
+ if (p->has_doc()) {
+ ss << " " << p->get_doc();
+ }
+ }
+ generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n");
+ }
+}
+
+std::string t_as3_generator::generate_isset_check(t_field* field) {
+ return generate_isset_check(field->get_name());
+}
+
+std::string t_as3_generator::generate_isset_check(std::string field_name) {
+ return "is" + get_cap_name("set") + get_cap_name(field_name) + "()";
+}
+
+void t_as3_generator::generate_isset_set(ostream& out, t_field* field) {
+ if (!type_can_be_null(field->get_type())) {
+ indent(out) << "this.__isset_" << field->get_name() << " = true;" << endl;
+ }
+}
+
+std::string t_as3_generator::get_enum_class_name(t_type* type) {
+ string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL && program != program_) {
+ package = program->get_namespace("as3") + ".";
+ }
+ return package + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ as3,
+ "AS3",
+ " bindable: Add [bindable] metadata to all the struct classes.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc
new file mode 100644
index 000000000..f1531cc83
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc
@@ -0,0 +1,4583 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <ctype.h>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/* forward declarations */
+string initial_caps_to_underscores(string name);
+string underscores_to_initial_caps(string name);
+string to_upper_case(string name);
+string to_lower_case(string name);
+
+/**
+ * C code generator, using glib for C typing.
+ */
+class t_c_glib_generator : public t_oop_generator {
+public:
+ /* constructor */
+ t_c_glib_generator(t_program* program,
+ const map<string, string>& parsed_options,
+ const string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* set the output directory */
+ this->out_dir_base_ = "gen-c_glib";
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option c_glib:" + iter->first;
+ }
+
+ /* set the namespace */
+ this->nspace = program_->get_namespace("c_glib");
+
+ if (this->nspace.empty()) {
+ this->nspace = "";
+ this->nspace_u = "";
+ this->nspace_uc = "";
+ this->nspace_lc = "";
+ } else {
+ /* replace dots with underscores */
+ char* tmp = strdup(this->nspace.c_str());
+ for (unsigned int i = 0; i < strlen(tmp); i++) {
+ if (tmp[i] == '.') {
+ tmp[i] = '_';
+ }
+ }
+ this->nspace = string(tmp, strlen(tmp));
+ free(tmp);
+
+ /* clean up the namespace for C.
+ * An input of 'namespace foo' should result in:
+ * - nspace = foo - for thrift objects and typedefs
+ * - nspace_u = Foo - for internal GObject prefixes
+ * - nspace_uc = FOO_ - for macro prefixes
+ * - nspace_lc = foo_ - for filename and method prefixes
+ * The underscores are there since uc and lc strings are used as file and
+ * variable prefixes.
+ */
+ this->nspace_u = initial_caps_to_underscores(this->nspace);
+ this->nspace_uc = to_upper_case(this->nspace_u) + "_";
+ this->nspace_lc = to_lower_case(this->nspace_u) + "_";
+ }
+ }
+
+ /* initialization and destruction */
+ void init_generator() override;
+ void close_generator() override;
+
+ /* generation functions */
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_consts(vector<t_const*> consts) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_service(t_service* tservice) override;
+ void generate_xception(t_struct* tstruct) override;
+
+private:
+ /* file streams */
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_types_impl_;
+ ofstream_with_content_based_conditional_update f_header_;
+ ofstream_with_content_based_conditional_update f_service_;
+
+ /* namespace variables */
+ string nspace;
+ string nspace_u;
+ string nspace_uc;
+ string nspace_lc;
+
+ /* helper functions */
+ bool is_complex_type(t_type* ttype);
+ bool is_numeric(t_type* ttype);
+ string type_name(t_type* ttype, bool in_typedef = false, bool is_const = false);
+ string property_type_name(t_type* ttype, bool in_typedef = false, bool is_const = false);
+ string base_type_name(t_type* type);
+ string type_to_enum(t_type* type);
+ string constant_literal(t_type* type, t_const_value* value);
+ string constant_value(string name, t_type* type, t_const_value* value);
+ string constant_value_with_storage(string name, t_type* type, t_const_value* value);
+ string function_signature(t_function* tfunction);
+ string argument_list(t_struct* tstruct);
+ string xception_list(t_struct* tstruct);
+ string declare_field(t_field* tfield,
+ bool init = false,
+ bool pointer = false,
+ bool constant = false,
+ bool reference = false);
+ void declare_local_variable(ostream& out, t_type* ttype, string& base_name, bool for_hash_table);
+ void declore_local_variable_for_write(ostream& out, t_type* ttype, string& base_name);
+
+ /* generation functions */
+ void generate_const_initializer(string name,
+ t_type* type,
+ t_const_value* value,
+ bool top_level = false);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_handler(t_service* tservice);
+ void generate_service_processor(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_object(t_struct* tstruct);
+ void generate_struct_writer(ostream& out,
+ t_struct* tstruct,
+ string this_name,
+ string this_get = "",
+ bool is_function = true);
+ void generate_struct_reader(ostream& out,
+ t_struct* tstruct,
+ string this_name,
+ string this_get = "",
+ bool is_function = true);
+
+ void generate_serialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ string suffix,
+ int error_ret);
+ void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix, int error_ret);
+ void generate_serialize_container(ostream& out, t_type* ttype, string prefix, int error_ret);
+ void generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string key,
+ string value,
+ int error_ret);
+ void generate_serialize_set_element(ostream& out, t_set* tset, string element, int error_ret);
+ void generate_serialize_list_element(ostream& out,
+ t_list* tlist,
+ string list,
+ string index,
+ int error_ret);
+
+ void generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ string suffix,
+ int error_ret,
+ bool allocate = true);
+ void generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix,
+ int error_ret,
+ bool allocate = true);
+ void generate_deserialize_container(ostream& out, t_type* ttype, string prefix, int error_ret);
+ void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix, int error_ret);
+ void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix, int error_ret);
+ void generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix,
+ string index,
+ int error_ret);
+
+ string generate_new_hash_from_type(t_type* key, t_type* value);
+ string generate_new_array_from_type(t_type* ttype);
+
+ string generate_free_func_from_type(t_type* ttype);
+ string generate_hash_func_from_type(t_type* ttype);
+ string generate_cmp_func_from_type(t_type* ttype);
+};
+
+/**
+ * Prepare for file generation by opening up the necessary file
+ * output streams.
+ */
+void t_c_glib_generator::init_generator() {
+ /* create output directory */
+ MKDIR(get_out_dir().c_str());
+
+ string program_name_u = initial_caps_to_underscores(program_name_);
+ string program_name_uc = to_upper_case(program_name_u);
+ string program_name_lc = to_lower_case(program_name_u);
+
+ /* create output files */
+ string f_types_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.h";
+ f_types_.open(f_types_name.c_str());
+ string f_types_impl_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.c";
+ f_types_impl_.open(f_types_impl_name.c_str());
+
+ /* add thrift boilerplate headers */
+ f_types_ << autogen_comment();
+ f_types_impl_ << autogen_comment();
+
+ /* include inclusion guard */
+ f_types_ << "#ifndef " << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << "#define "
+ << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << endl;
+
+ /* include base types */
+ f_types_ << "/* base includes */" << endl << "#include <glib-object.h>" << endl
+ << "#include <thrift/c_glib/thrift_struct.h>" << endl
+ << "#include <thrift/c_glib/protocol/thrift_protocol.h>" << endl;
+
+ /* include other thrift includes */
+ const vector<t_program*>& includes = program_->get_includes();
+ if (!includes.empty()) {
+ f_types_ << "/* other thrift includes */" << endl;
+
+ for (auto include : includes) {
+ const std::string& include_nspace = include->get_namespace("c_glib");
+ std::string include_nspace_prefix =
+ include_nspace.empty() ? "" : initial_caps_to_underscores(include_nspace) + "_";
+
+ f_types_ << "#include \"" << include_nspace_prefix
+ << initial_caps_to_underscores(include->get_name()) << "_types.h\"" << endl;
+ }
+ f_types_ << endl;
+ }
+
+ /* include custom headers */
+ const vector<string>& c_includes = program_->get_c_includes();
+ f_types_ << "/* custom thrift includes */" << endl;
+ for (const auto & c_include : c_includes) {
+ if (c_include[0] == '<') {
+ f_types_ << "#include " << c_include << endl;
+ } else {
+ f_types_ << "#include \"" << c_include << "\"" << endl;
+ }
+ }
+ f_types_ << endl;
+
+ /* include math.h (for "INFINITY") in the implementation file, in case we
+ encounter a struct with a member of type double */
+ f_types_impl_ << endl << "#include <math.h>" << endl;
+
+ // include the types file
+ f_types_impl_ << endl << "#include \"" << this->nspace_lc << program_name_u << "_types.h\""
+ << endl << "#include <thrift/c_glib/thrift.h>" << endl << endl;
+
+ f_types_ << "/* begin types */" << endl << endl;
+}
+
+/**
+ * Finish up generation and close all file streams.
+ */
+void t_c_glib_generator::close_generator() {
+ string program_name_uc = to_upper_case(initial_caps_to_underscores(program_name_));
+
+ /* end the header inclusion guard */
+ f_types_ << "#endif /* " << this->nspace_uc << program_name_uc << "_TYPES_H */" << endl;
+
+ /* close output file */
+ f_types_.close();
+ f_types_impl_.close();
+}
+
+/**
+ * Generates a Thrift typedef in C code. For example:
+ *
+ * Thrift:
+ * typedef map<i32,i32> SomeMap
+ *
+ * C:
+ * typedef GHashTable * ThriftSomeMap;
+ */
+void t_c_glib_generator::generate_typedef(t_typedef* ttypedef) {
+ f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " << this->nspace
+ << ttypedef->get_symbolic() << ";" << endl << endl;
+}
+
+/**
+ * Generates a C enumeration. For example:
+ *
+ * Thrift:
+ * enum MyEnum {
+ * ONE = 1,
+ * TWO
+ * }
+ *
+ * C:
+ * enum _ThriftMyEnum {
+ * THRIFT_MY_ENUM_ONE = 1,
+ * THRIFT_MY_ENUM_TWO
+ * };
+ * typedef enum _ThriftMyEnum ThriftMyEnum;
+ */
+void t_c_glib_generator::generate_enum(t_enum* tenum) {
+ string name = tenum->get_name();
+ string name_uc = to_upper_case(initial_caps_to_underscores(name));
+
+ f_types_ << indent() << "enum _" << this->nspace << name << " {" << endl;
+
+ indent_up();
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ bool first = true;
+
+ /* output each of the enumeration elements */
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_types_ << "," << endl;
+ }
+
+ f_types_ << indent() << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name();
+ f_types_ << " = " << (*c_iter)->get_value();
+ }
+
+ indent_down();
+ f_types_ << endl << "};" << endl << "typedef enum _" << this->nspace << name << " "
+ << this->nspace << name << ";" << endl << endl;
+
+ f_types_ << "/* return the name of the constant */" << endl;
+ f_types_ << "const char *" << endl;
+ f_types_ << "toString_" << name << "(int value); " << endl << endl;
+ ;
+ f_types_impl_ << "/* return the name of the constant */" << endl;
+ f_types_impl_ << "const char *" << endl;
+ f_types_impl_ << "toString_" << name << "(int value) " << endl;
+ f_types_impl_ << "{" << endl;
+ f_types_impl_ << " static __thread char buf[16] = {0};" << endl;
+ f_types_impl_ << " switch(value) {" << endl;
+ std::set<int> done;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ // Skipping duplicate value
+ if (done.find(value) == done.end()) {
+ done.insert(value);
+ f_types_impl_ << " case " << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name()
+ << ":"
+ << "return \"" << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name()
+ << "\";" << endl;
+ }
+ }
+ f_types_impl_ << " default: g_snprintf(buf, 16, \"%d\", value); return buf;" << endl;
+ f_types_impl_ << " }" << endl;
+ f_types_impl_ << "}" << endl << endl;
+}
+
+/**
+ * Generates Thrift constants in C code.
+ */
+void t_c_glib_generator::generate_consts(vector<t_const*> consts) {
+ f_types_ << "/* constants */" << endl;
+ f_types_impl_ << "/* constants */" << endl;
+
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ string name = (*c_iter)->get_name();
+ string name_uc = to_upper_case(name);
+ string name_lc = to_lower_case(name);
+ t_type* type = (*c_iter)->get_type();
+ t_const_value* value = (*c_iter)->get_value();
+
+ if (is_complex_type(type)) {
+ f_types_ << type_name(type) << indent() << this->nspace_lc << name_lc
+ << "_constant();" << endl;
+ }
+
+ f_types_ << indent() << "#define " << this->nspace_uc << name_uc << " "
+ << constant_value(name_lc, type, value) << endl;
+
+ generate_const_initializer(name_lc, type, value, true);
+ }
+
+ f_types_ << endl;
+ f_types_impl_ << endl;
+}
+
+/**
+ * Generate Thrift structs in C code, as GObjects. Example:
+ *
+ * Thrift:
+ * struct Bonk
+ * {
+ * 1: string message,
+ * 2: i32 type
+ * }
+ *
+ * C GObject instance header:
+ * struct _ThriftBonk
+ * {
+ * GObject parent;
+ *
+ * gchar * message;
+ * gint32 type;
+ * };
+ * typedef struct _ThriftBonk ThriftBonk
+ * // ... additional GObject boilerplate ...
+ */
+void t_c_glib_generator::generate_struct(t_struct* tstruct) {
+ f_types_ << "/* struct " << tstruct->get_name() << " */" << endl;
+ generate_object(tstruct);
+}
+
+/**
+ * Generate C code to represent Thrift services. Creates a new GObject
+ * which can be used to access the service.
+ */
+void t_c_glib_generator::generate_service(t_service* tservice) {
+ string svcname_u = initial_caps_to_underscores(tservice->get_name());
+ string svcname_uc = this->nspace_uc + to_upper_case(svcname_u);
+ string filename = this->nspace_lc + to_lower_case(svcname_u);
+
+ // make output files
+ string f_header_name = get_out_dir() + filename + ".h";
+ f_header_.open(f_header_name.c_str());
+
+ string program_name_u = initial_caps_to_underscores(program_name_);
+ string program_name_lc = to_lower_case(program_name_u);
+
+ // add header file boilerplate
+ f_header_ << autogen_comment();
+
+ // add an inclusion guard
+ f_header_ << "#ifndef " << svcname_uc << "_H" << endl << "#define " << svcname_uc << "_H" << endl
+ << endl;
+
+ // add standard includes
+ f_header_ << "#include <thrift/c_glib/processor/thrift_dispatch_processor.h>" << endl << endl;
+ f_header_ << "#include \"" << this->nspace_lc << program_name_lc << "_types.h\"" << endl;
+
+ // if we are inheriting from another service, include its header
+ t_service* extends_service = tservice->get_extends();
+ if (extends_service != NULL) {
+ f_header_ << "#include \"" << this->nspace_lc
+ << to_lower_case(initial_caps_to_underscores(extends_service->get_name())) << ".h\""
+ << endl;
+ }
+ f_header_ << endl;
+
+ // create the service implementation
+ string f_service_name = get_out_dir() + filename + ".c";
+ f_service_.open(f_service_name.c_str());
+
+ // add the boilerplace header
+ f_service_ << autogen_comment();
+
+ // include the headers
+ f_service_ << "#include <string.h>" << endl << "#include <thrift/c_glib/thrift.h>" << endl
+ << "#include <thrift/c_glib/thrift_application_exception.h>" << endl << "#include \""
+ << filename << ".h\"" << endl << endl;
+
+ // generate the service-helper classes
+ generate_service_helpers(tservice);
+
+ // generate the client objects
+ generate_service_client(tservice);
+
+ // generate the server objects
+ generate_service_server(tservice);
+
+ // end the header inclusion guard
+ f_header_ << "#endif /* " << svcname_uc << "_H */" << endl;
+
+ // close the files
+ f_service_.close();
+ f_header_.close();
+}
+
+/**
+ *
+ */
+void t_c_glib_generator::generate_xception(t_struct* tstruct) {
+ string name = tstruct->get_name();
+ string name_u = initial_caps_to_underscores(name);
+ string name_lc = to_lower_case(name_u);
+ string name_uc = to_upper_case(name_u);
+
+ generate_object(tstruct);
+
+ f_types_ << "/* exception */" << endl
+ << "typedef enum" << endl
+ << "{" << endl;
+ indent_up();
+ f_types_ << indent() << this->nspace_uc << name_uc << "_ERROR_CODE" << endl;
+ indent_down();
+ f_types_ << "} " << this->nspace << name << "Error;" << endl
+ << endl
+ << "GQuark " << this->nspace_lc << name_lc
+ << "_error_quark (void);" << endl
+ << "#define " << this->nspace_uc << name_uc << "_ERROR ("
+ << this->nspace_lc << name_lc << "_error_quark())" << endl
+ << endl
+ << endl;
+
+ f_types_impl_ << "/* define the GError domain for exceptions */" << endl << "#define "
+ << this->nspace_uc << name_uc << "_ERROR_DOMAIN \"" << this->nspace_lc << name_lc
+ << "_error_quark\"" << endl << "GQuark" << endl << this->nspace_lc << name_lc
+ << "_error_quark (void)" << endl << "{" << endl
+ << " return g_quark_from_static_string (" << this->nspace_uc << name_uc
+ << "_ERROR_DOMAIN);" << endl << "}" << endl << endl;
+}
+
+/********************
+ * HELPER FUNCTIONS *
+ ********************/
+
+/**
+ * Returns true if ttype is not a primitive.
+ */
+bool t_c_glib_generator::is_complex_type(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception();
+}
+
+bool t_c_glib_generator::is_numeric(t_type* ttype) {
+ return ttype->is_enum() || (ttype->is_base_type() && !ttype->is_string());
+}
+
+/**
+ * Maps a Thrift t_type to a C type.
+ */
+string t_c_glib_generator::type_name(t_type* ttype, bool in_typedef, bool is_const) {
+ if (ttype->is_base_type()) {
+ string bname = base_type_name(ttype);
+
+ if (is_const) {
+ return "const " + bname;
+ } else {
+ return bname;
+ }
+ }
+
+ if (ttype->is_container()) {
+ string cname;
+
+ t_container* tcontainer = (t_container*)ttype;
+ if (tcontainer->has_cpp_name()) {
+ cname = tcontainer->get_cpp_name();
+ } else if (ttype->is_map()) {
+ cname = "GHashTable";
+ } else if (ttype->is_set()) {
+ // since a set requires unique elements, use a GHashTable, and
+ // populate the keys and values with the same data, using keys for
+ // the actual writes and reads.
+ // TODO: discuss whether or not to implement TSet, THashSet or GHashSet
+ cname = "GHashTable";
+ } else if (ttype->is_list()) {
+ t_type* etype = get_true_type(((t_list*)ttype)->get_elem_type());
+ if (etype->is_void()) {
+ throw std::runtime_error("compiler error: list element type cannot be void");
+ }
+ // TODO: investigate other implementations besides GPtrArray
+ cname = is_numeric(etype) ? "GArray" : "GPtrArray";
+ }
+
+ /* Omit the dereference operator if we are aliasing this type within a
+ typedef, to allow the type to be used more naturally in client code;
+ otherwise, include it */
+ if (!in_typedef) {
+ cname += " *";
+ }
+
+ if (is_const) {
+ return "const " + cname;
+ } else {
+ return cname;
+ }
+ }
+
+ // check for a namespace
+ t_program* tprogram = ttype->get_program();
+ string pname = (tprogram ? tprogram->get_namespace("c_glib") : "") + ttype->get_name();
+
+ if (is_complex_type(ttype)) {
+ pname += " *";
+ }
+
+ if (is_const) {
+ return "const " + pname;
+ } else {
+ return pname;
+ }
+}
+
+/**
+ * Maps a Thrift primitive to the type needed to hold its value when used as an
+ * object property.
+ *
+ * This method is needed because all integer properties of width less than 64
+ * bits map to the same type, gint, as opposed to their width-specific type
+ * (gint8, gint16 or gint32).
+ */
+string t_c_glib_generator::property_type_name(t_type* ttype, bool in_typedef, bool is_const) {
+ string result;
+
+ if (ttype->is_base_type()) {
+ switch (((t_base_type*)ttype)->get_base()) {
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ if (is_const) {
+ result = "const gint";
+ } else {
+ result = "gint";
+ }
+ break;
+
+ default:
+ result = type_name(ttype, in_typedef, is_const);
+ }
+ } else {
+ result = type_name(ttype, in_typedef, is_const);
+ }
+
+ return result;
+}
+
+/**
+ * Maps a Thrift primitive to a C primitive.
+ */
+string t_c_glib_generator::base_type_name(t_type* type) {
+ if (type->is_enum()) {
+ return type_name(type);
+ }
+ if (!type->is_base_type()) {
+ throw std::invalid_argument("Only base types are suppported.");
+ }
+ t_base_type* base_type = reinterpret_cast<t_base_type*>(type);
+ t_base_type::t_base tbase = base_type->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ if (base_type->is_binary()) {
+ return "GByteArray *";
+ } else {
+ return "gchar *";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "gboolean";
+ case t_base_type::TYPE_I8:
+ return "gint8";
+ case t_base_type::TYPE_I16:
+ return "gint16";
+ case t_base_type::TYPE_I32:
+ return "gint32";
+ case t_base_type::TYPE_I64:
+ return "gint64";
+ case t_base_type::TYPE_DOUBLE:
+ return "gdouble";
+ default:
+ throw std::logic_error("compiler error: no C base type name for base type "
+ + t_base_type::t_base_name(tbase));
+ }
+}
+
+/**
+ * Returns a member of the ThriftType C enumeration in thrift_protocol.h
+ * for a Thrift type.
+ */
+string t_c_glib_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "T_STRING";
+ case t_base_type::TYPE_BOOL:
+ return "T_BOOL";
+ case t_base_type::TYPE_I8:
+ return "T_BYTE";
+ case t_base_type::TYPE_I16:
+ return "T_I16";
+ case t_base_type::TYPE_I32:
+ return "T_I32";
+ case t_base_type::TYPE_I64:
+ return "T_I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "T_DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "T_I32";
+ } else if (type->is_struct()) {
+ return "T_STRUCT";
+ } else if (type->is_xception()) {
+ return "T_STRUCT";
+ } else if (type->is_map()) {
+ return "T_MAP";
+ } else if (type->is_set()) {
+ return "T_SET";
+ } else if (type->is_list()) {
+ return "T_LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Returns a Thrift constant formatted as a literal for inclusion in C code.
+ */
+string t_c_glib_generator::constant_literal(t_type* type, t_const_value* value) {
+ ostringstream render;
+
+ if (type->is_base_type()) {
+ /* primitives */
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << "\"" + value->get_string() + "\"";
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() != 0) ? "TRUE" : "FALSE");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ render << value->get_double();
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else {
+ t_const_value::t_const_value_type value_type = value->get_type();
+
+ switch (value_type) {
+ case t_const_value::CV_IDENTIFIER:
+ render << value->get_integer();
+ break;
+ case t_const_value::CV_LIST:
+ render << "{ ";
+ {
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ const vector<t_const_value*>& list = value->get_list();
+ vector<t_const_value*>::const_iterator list_iter;
+
+ if (list.size() > 0) {
+ list_iter = list.begin();
+ render << constant_literal(elem_type, *list_iter);
+
+ while (++list_iter != list.end()) {
+ render << ", " << constant_literal(elem_type, *list_iter);
+ }
+ }
+ }
+ render << " }";
+ break;
+ case t_const_value::CV_MAP:
+ default:
+ render << "NULL /* not supported */";
+ }
+ }
+
+ return render.str();
+}
+
+/**
+ * Returns C code that represents a Thrift constant.
+ */
+string t_c_glib_generator::constant_value(string name, t_type* type, t_const_value* value) {
+ ostringstream render;
+
+ if (type->is_base_type()) {
+ /* primitives */
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << "g_strdup (\"" + value->get_string() + "\")";
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() != 0) ? 1 : 0);
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ render << "G_GINT64_CONSTANT (" << value->get_integer() << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << value->get_integer();
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << "(" << type_name(type) << ")" << value->get_integer();
+ } else if (is_complex_type(type)) {
+ render << "(" << this->nspace_lc << to_lower_case(name) << "_constant())";
+ } else {
+ render << "NULL /* not supported */";
+ }
+
+ return render.str();
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_c_glib_generator::function_signature(t_function* tfunction) {
+ t_type* ttype = tfunction->get_returntype();
+ t_struct* arglist = tfunction->get_arglist();
+ t_struct* xlist = tfunction->get_xceptions();
+ string fname = initial_caps_to_underscores(tfunction->get_name());
+
+ bool has_return = !ttype->is_void();
+ bool has_args = arglist->get_members().size() == 0;
+ bool has_xceptions = xlist->get_members().size() == 0;
+ return "gboolean " + this->nspace_lc + fname + " (" + this->nspace + service_name_ + "If * iface"
+ + (has_return ? ", " + type_name(ttype) + "* _return" : "")
+ + (has_args ? "" : (", " + argument_list(arglist)))
+ + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError ** error)";
+}
+
+/**
+ * Renders a field list
+ *
+ * @param tstruct The struct definition
+ * @return Comma sepearated list of all field names in that struct
+ */
+string t_c_glib_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type(), false, true) + " " + (*f_iter)->get_name();
+ }
+ return result;
+}
+
+/**
+ * Renders mutable exception lists
+ *
+ * @param tstruct The struct definition
+ * @return Comma sepearated list of all field names in that struct
+ */
+string t_c_glib_generator::xception_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type(), false, false) + "* " + (*f_iter)->get_name();
+ }
+ return result;
+}
+
+/**
+ * Declares a field, including any necessary initialization.
+ */
+string t_c_glib_generator::declare_field(t_field* tfield,
+ bool init,
+ bool pointer,
+ bool constant,
+ bool reference) {
+ string result = "";
+ if (constant) {
+ result += "const ";
+ }
+ result += type_name(tfield->get_type());
+ if (pointer) {
+ result += "*";
+ }
+ if (reference) {
+ result += "*";
+ }
+ result += " " + tfield->get_name();
+ if (init) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ break;
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (gdouble) 0";
+ break;
+ case t_base_type::TYPE_STRING:
+ result += " = NULL";
+ break;
+ default:
+ throw "compiler error: no C intializer for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ result += " = (" + type_name(type) + ") 0";
+ } else if (type->is_struct() || type->is_container()) {
+ result += " = NULL";
+ }
+ }
+
+ if (!reference) {
+ result += ";";
+ }
+
+ return result;
+}
+
+string t_c_glib_generator::constant_value_with_storage(string fname,
+ t_type* etype,
+ t_const_value* value) {
+ ostringstream render;
+ if (is_numeric(etype)) {
+ render << " " << type_name(etype) << " *" << fname << " = "
+ << "g_new (" << base_type_name(etype) << ", 1);" << endl
+ << " *" << fname << " = " << constant_value(fname, (t_type*)etype, value) << ";"
+ << endl;
+ } else {
+ render << " " << type_name(etype) << " " << fname << " = "
+ << constant_value(fname, (t_type*)etype, value) << ";" << endl;
+ }
+ return render.str();
+}
+
+/**
+ * Generates C code that initializes complex constants.
+ */
+void t_c_glib_generator::generate_const_initializer(string name,
+ t_type* type,
+ t_const_value* value,
+ bool top_level) {
+ string name_u = initial_caps_to_underscores(name);
+ string name_lc = to_lower_case(name_u);
+ string type_u = initial_caps_to_underscores(type->get_name());
+ string type_uc = to_upper_case(type_u);
+ string maybe_static = top_level ? "" : "static ";
+
+ if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ ostringstream initializers;
+
+ // initialize any constants that may be referenced by this initializer
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ string field_name = "";
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ field_name = (*f_iter)->get_name();
+ break;
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field "
+ + v_iter->first->get_string();
+ }
+ field_name = tmp(field_name);
+
+ generate_const_initializer(name + "_constant_" + field_name,
+ field_type,
+ v_iter->second);
+ initializers << " constant->" << v_iter->first->get_string() << " = "
+ << constant_value(name + "_constant_" + field_name,
+ field_type,
+ v_iter->second) << ";" << endl
+ << " constant->__isset_" << v_iter->first->get_string()
+ << " = TRUE;" << endl;
+ }
+
+ // implement the initializer
+ f_types_impl_ << maybe_static << this->nspace << type->get_name() << " *"
+ << endl
+ << this->nspace_lc << name_lc << "_constant (void)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << "static " << this->nspace << type->get_name()
+ << " *constant = NULL;" << endl
+ << indent() << "if (constant == NULL)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << "constant = g_object_new (" << this->nspace_uc
+ << "TYPE_" << type_uc << ", NULL);" << endl
+ << initializers.str();
+ scope_down(f_types_impl_);
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ string field_name = "";
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ field_name = (*f_iter)->get_name();
+ break;
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field "
+ + v_iter->first->get_string();
+ }
+ field_name = tmp(field_name);
+ }
+
+ f_types_impl_ << indent() << "return constant;" << endl;
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+ } else if (type->is_list()) {
+ string list_type = "GPtrArray *";
+ string free_func
+ = generate_free_func_from_type(reinterpret_cast<t_list*>(type)->get_elem_type());
+ string list_initializer = "g_ptr_array_new_with_free_func (" + free_func + ");";
+ string list_appender = "g_ptr_array_add";
+ bool list_variable = false;
+
+ t_type* etype = ((t_list*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ ostringstream initializers;
+ ostringstream appenders;
+
+ list_initializer = generate_new_array_from_type(etype);
+ if (etype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot determine array type";
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ list_type = "GArray *";
+ list_appender = "g_array_append_val";
+ list_variable = true;
+ break;
+ case t_base_type::TYPE_STRING:
+ break;
+ default:
+ throw "compiler error: no array info for type";
+ }
+ } else if (etype->is_enum()) {
+ list_type = "GArray *";
+ list_appender = "g_array_append_val";
+ list_variable = true;
+ }
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string fname = tmp(name);
+
+ generate_const_initializer(fname, etype, (*v_iter));
+ if (list_variable) {
+ initializers << " " << type_name(etype) << " " << fname << " = "
+ << constant_value(fname, (t_type*)etype, (*v_iter)) << ";"
+ << endl;
+ appenders << " " << list_appender << "(constant, " << fname << ");"
+ << endl;
+ } else {
+ appenders << " " << list_appender << "(constant, "
+ << constant_value(fname, (t_type*)etype, (*v_iter)) << ");"
+ << endl;
+ }
+ }
+
+ f_types_impl_ << maybe_static << list_type << endl
+ << this->nspace_lc << name_lc << "_constant (void)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << "static " << list_type << " constant = NULL;"
+ << endl
+ << indent() << "if (constant == NULL)" << endl;
+ scope_up(f_types_impl_);
+ if (!initializers.str().empty()) {
+ f_types_impl_ << initializers.str()
+ << endl;
+ }
+ f_types_impl_ << indent() << "constant = " << list_initializer << endl
+ << appenders.str();
+ scope_down(f_types_impl_);
+ f_types_impl_ << indent() << "return constant;" << endl;
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ ostringstream initializers;
+ ostringstream appenders;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string fname = tmp(name);
+ string ptr = is_numeric(etype) ? "*" : "";
+ generate_const_initializer(fname, etype, (*v_iter));
+ initializers << constant_value_with_storage(fname, (t_type*)etype, *v_iter);
+ appenders << " g_hash_table_insert (constant, " << fname << ", 0);" << endl;
+ }
+
+ f_types_impl_ << maybe_static << "GHashTable *" << endl
+ << this->nspace_lc << name_lc << "_constant (void)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl
+ << indent() << "if (constant == NULL)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << initializers.str() << endl
+ << indent() << "constant = " << generate_new_hash_from_type(etype, NULL) << endl
+ << appenders.str();
+ scope_down(f_types_impl_);
+ f_types_impl_ << indent() << "return constant;" << endl;
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ ostringstream initializers;
+ ostringstream appenders;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string fname = tmp(name);
+ string kname = fname + "key";
+ string vname = fname + "val";
+ generate_const_initializer(kname, ktype, v_iter->first);
+ generate_const_initializer(vname, vtype, v_iter->second);
+
+ initializers << constant_value_with_storage(kname, (t_type*)ktype, v_iter->first);
+ initializers << constant_value_with_storage(vname, (t_type*)vtype, v_iter->second);
+ appenders << " g_hash_table_insert (constant, " << kname << ", " << vname << ");" << endl;
+ }
+
+ f_types_impl_ << maybe_static << "GHashTable *" << endl
+ << this->nspace_lc << name_lc << "_constant (void)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl
+ << indent() << "if (constant == NULL)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << initializers.str() << endl
+ << indent() << "constant = " << generate_new_hash_from_type(ktype, vtype) << endl
+ << appenders.str();
+ scope_down(f_types_impl_);
+ f_types_impl_ << indent() << "return constant;" << endl;
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+ }
+}
+
+/**
+ * Generates helper classes for a service, consisting of a ThriftStruct subclass
+ * for the arguments to and the result from each method.
+ *
+ * @param tservice The service for which to generate helper classes
+ */
+void t_c_glib_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator function_iter;
+
+ // Iterate through the service's methods
+ for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
+ string function_name = (*function_iter)->get_name();
+ t_struct* arg_list = (*function_iter)->get_arglist();
+ string arg_list_name_orig = arg_list->get_name();
+
+ // Generate the arguments class
+ arg_list->set_name(tservice->get_name() + underscores_to_initial_caps(function_name) + "Args");
+ generate_struct(arg_list);
+
+ arg_list->set_name(arg_list_name_orig);
+
+ // Generate the result class
+ if (!(*function_iter)->is_oneway()) {
+ t_struct result(program_,
+ tservice->get_name() + underscores_to_initial_caps(function_name) + "Result");
+ t_field success((*function_iter)->get_returntype(), "success", 0);
+ success.set_req(t_field::T_OPTIONAL);
+ if (!(*function_iter)->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = (*function_iter)->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator field_iter;
+ for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) {
+ (*field_iter)->set_req(t_field::T_OPTIONAL);
+ result.append(*field_iter);
+ }
+
+ generate_struct(&result);
+ }
+ }
+}
+
+/**
+ * Generates C code that represents a Thrift service client.
+ */
+void t_c_glib_generator::generate_service_client(t_service* tservice) {
+ /* get some C friendly service names */
+ string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
+ string service_name_uc = to_upper_case(service_name_lc);
+
+ string parent_service_name;
+ string parent_service_name_lc;
+ string parent_service_name_uc;
+
+ string parent_class_name = "GObject";
+ string parent_type_name = "G_TYPE_OBJECT";
+
+ // The service this service extends, or NULL if it extends no
+ // service
+ t_service* extends_service = tservice->get_extends();
+ if (extends_service) {
+ // The name of the parent service
+ parent_service_name = extends_service->get_name();
+ parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name));
+ parent_service_name_uc = to_upper_case(parent_service_name_lc);
+
+ // The names of the client class' parent class and type
+ parent_class_name = this->nspace + parent_service_name + "Client";
+ parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_CLIENT";
+ }
+
+ // The base service (the topmost in the "extends" hierarchy), on
+ // whose client class the "input_protocol" and "output_protocol"
+ // properties are defined
+ t_service* base_service = tservice;
+ while (base_service->get_extends()) {
+ base_service = base_service->get_extends();
+ }
+
+ string base_service_name = base_service->get_name();
+ string base_service_name_lc = to_lower_case(initial_caps_to_underscores(base_service_name));
+ string base_service_name_uc = to_upper_case(base_service_name_lc);
+
+ // Generate the client interface dummy object in the header.
+ f_header_ << "/* " << service_name_ << " service interface */" << endl << "typedef struct _"
+ << this->nspace << service_name_ << "If " << this->nspace << service_name_ << "If; "
+ << " /* dummy object */" << endl << endl;
+
+ // Generate the client interface object in the header.
+ f_header_ << "struct _" << this->nspace << service_name_ << "IfInterface" << endl << "{" << endl
+ << " GTypeInterface parent;" << endl << endl;
+
+ /* write out the functions for this interface */
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ /* make the function name C friendly */
+ string funname = initial_caps_to_underscores((*f_iter)->get_name());
+ t_type* ttype = (*f_iter)->get_returntype();
+ t_struct* arglist = (*f_iter)->get_arglist();
+ t_struct* xlist = (*f_iter)->get_xceptions();
+ bool has_return = !ttype->is_void();
+ bool has_args = arglist->get_members().size() == 0;
+ bool has_xceptions = xlist->get_members().size() == 0;
+
+ string params = "(" + this->nspace + service_name_ + "If *iface"
+ + (has_return ? ", " + type_name(ttype) + "* _return" : "")
+ + (has_args ? "" : (", " + argument_list(arglist)))
+ + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)";
+
+ indent(f_header_) << "gboolean (*" << funname << ") " << params << ";" << endl;
+ }
+ indent_down();
+
+ f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "IfInterface "
+ << this->nspace << service_name_ << "IfInterface;" << endl << endl;
+
+ // generate all the interface boilerplate
+ f_header_ << "GType " << this->nspace_lc << service_name_lc << "_if_get_type (void);" << endl
+ << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF "
+ << "(" << this->nspace_lc << service_name_lc << "_if_get_type())" << endl << "#define "
+ << this->nspace_uc << service_name_uc << "_IF(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_IF, " << this->nspace << service_name_ << "If))" << endl
+ << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_IF(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_IF))" << endl << "#define " << this->nspace_uc
+ << service_name_uc << "_IF_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), "
+ << this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << this->nspace
+ << service_name_ << "IfInterface))" << endl << endl;
+
+ // write out all the interface function prototypes
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ /* make the function name C friendly */
+ string funname = initial_caps_to_underscores((*f_iter)->get_name());
+ t_type* ttype = (*f_iter)->get_returntype();
+ t_struct* arglist = (*f_iter)->get_arglist();
+ t_struct* xlist = (*f_iter)->get_xceptions();
+ bool has_return = !ttype->is_void();
+ bool has_args = arglist->get_members().size() == 0;
+ bool has_xceptions = xlist->get_members().size() == 0;
+
+ string params = "(" + this->nspace + service_name_ + "If *iface"
+ + (has_return ? ", " + type_name(ttype) + "* _return" : "")
+ + (has_args ? "" : (", " + argument_list(arglist)))
+ + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)";
+
+ f_header_ << "gboolean " << this->nspace_lc << service_name_lc << "_if_" << funname << " "
+ << params << ";" << endl;
+ }
+ f_header_ << endl;
+
+ // Generate the client object instance definition in the header.
+ f_header_ << "/* " << service_name_ << " service client */" << endl << "struct _" << this->nspace
+ << service_name_ << "Client" << endl << "{" << endl << " " << parent_class_name
+ << " parent;" << endl;
+ if (!extends_service) {
+ // Define "input_protocol" and "output_protocol" properties only
+ // for base services; child service-client classes will inherit
+ // these
+ f_header_ << endl << " ThriftProtocol *input_protocol;" << endl
+ << " ThriftProtocol *output_protocol;" << endl;
+ }
+ f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "Client "
+ << this->nspace << service_name_ << "Client;" << endl << endl;
+
+ // Generate the class definition in the header.
+ f_header_ << "struct _" << this->nspace << service_name_ << "ClientClass" << endl << "{" << endl
+ << " " << parent_class_name << "Class parent;" << endl << "};" << endl
+ << "typedef struct _" << this->nspace << service_name_ << "ClientClass " << this->nspace
+ << service_name_ << "ClientClass;" << endl << endl;
+
+ // Create all the GObject boilerplate
+ f_header_ << "GType " << this->nspace_lc << service_name_lc << "_client_get_type (void);" << endl
+ << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT "
+ << "(" << this->nspace_lc << service_name_lc << "_client_get_type())" << endl
+ << "#define " << this->nspace_uc << service_name_uc << "_CLIENT(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "Client))" << endl
+ << "#define " << this->nspace_uc << service_name_uc << "_CLIENT_CLASS(c) "
+ << "(G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
+ << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))" << endl << "#define "
+ << this->nspace_uc << service_name_uc << "_IS_CLIENT(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_CLIENT))" << endl << "#define " << this->nspace_uc
+ << service_name_uc << "_IS_CLIENT_CLASS(c) "
+ << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
+ << "_CLIENT))" << endl << "#define " << this->nspace_uc << service_name_uc
+ << "_CLIENT_GET_CLASS(obj) "
+ << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))"
+ << endl << endl;
+
+ /* write out the function prototypes */
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ /* make the function name C friendly */
+ string funname = to_lower_case(initial_caps_to_underscores((*f_iter)->get_name()));
+
+ t_function service_function((*f_iter)->get_returntype(),
+ service_name_lc + string("_client_") + funname,
+ (*f_iter)->get_arglist(),
+ (*f_iter)->get_xceptions());
+ indent(f_header_) << function_signature(&service_function) << ";" << endl;
+
+ t_function send_function(g_type_void,
+ service_name_lc + string("_client_send_") + funname,
+ (*f_iter)->get_arglist());
+ indent(f_header_) << function_signature(&send_function) << ";" << endl;
+
+ // implement recv if not a oneway service
+ if (!(*f_iter)->is_oneway()) {
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ service_name_lc + string("_client_recv_") + funname,
+ &noargs,
+ (*f_iter)->get_xceptions());
+ indent(f_header_) << function_signature(&recv_function) << ";" << endl;
+ }
+ }
+
+ /* write out the get/set function prototypes */
+ f_header_ << "void " + service_name_lc + "_client_set_property (GObject *object, guint "
+ "property_id, const GValue *value, GParamSpec *pspec);"
+ << endl;
+ f_header_ << "void " + service_name_lc + "_client_get_property (GObject *object, guint "
+ "property_id, GValue *value, GParamSpec *pspec);"
+ << endl;
+
+ f_header_ << endl;
+ // end of header code
+
+ // Generate interface method implementations
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ /* make the function name C friendly */
+ string funname = initial_caps_to_underscores((*f_iter)->get_name());
+ t_type* ttype = (*f_iter)->get_returntype();
+ t_struct* arglist = (*f_iter)->get_arglist();
+ t_struct* xlist = (*f_iter)->get_xceptions();
+ bool has_return = !ttype->is_void();
+ bool has_args = arglist->get_members().size() == 0;
+ bool has_xceptions = xlist->get_members().size() == 0;
+
+ string params = "(" + this->nspace + service_name_ + "If *iface"
+ + (has_return ? ", " + type_name(ttype) + "* _return" : "")
+ + (has_args ? "" : (", " + argument_list(arglist)))
+ + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)";
+
+ string params_without_type = string("iface, ") + (has_return ? "_return, " : "");
+
+ const vector<t_field*>& fields = arglist->get_members();
+ vector<t_field*>::const_iterator f_iter_field;
+ for (f_iter_field = fields.begin(); f_iter_field != fields.end(); ++f_iter_field) {
+ params_without_type += (*f_iter_field)->get_name();
+ params_without_type += ", ";
+ }
+
+ const vector<t_field*>& xceptions = xlist->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ params_without_type += (*x_iter)->get_name();
+ params_without_type += ", ";
+ }
+
+ f_service_ << "gboolean" << endl << this->nspace_lc << service_name_lc << "_if_" << funname
+ << " " << params << endl << "{" << endl << " return " << this->nspace_uc
+ << service_name_uc << "_IF_GET_INTERFACE (iface)->" << funname << " ("
+ << params_without_type << "error);" << endl << "}" << endl << endl;
+ }
+
+ // Generate interface boilerplate
+ f_service_ << "GType" << endl << this->nspace_lc << service_name_lc << "_if_get_type (void)"
+ << endl << "{" << endl << " static GType type = 0;" << endl << " if (type == 0)"
+ << endl << " {" << endl << " static const GTypeInfo type_info =" << endl << " {"
+ << endl << " sizeof (" << this->nspace << service_name_ << "IfInterface)," << endl
+ << " NULL, /* base_init */" << endl << " NULL, /* base_finalize */" << endl
+ << " NULL, /* class_init */" << endl << " NULL, /* class_finalize */"
+ << endl << " NULL, /* class_data */" << endl
+ << " 0, /* instance_size */" << endl << " 0, /* n_preallocs */"
+ << endl << " NULL, /* instance_init */" << endl
+ << " NULL /* value_table */" << endl << " };" << endl
+ << " type = g_type_register_static (G_TYPE_INTERFACE," << endl
+ << " \"" << this->nspace << service_name_ << "If\","
+ << endl << " &type_info, 0);" << endl << " }"
+ << endl << " return type;" << endl << "}" << endl << endl;
+
+ // Generate client boilerplate
+ f_service_ << "static void " << endl << this->nspace_lc << service_name_lc
+ << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);"
+ << endl << endl << "G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_
+ << "Client, " << this->nspace_lc << service_name_lc << "_client," << endl
+ << " " << parent_type_name << ", " << endl
+ << " G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_IF," << endl
+ << " " << this->nspace_lc
+ << service_name_lc << "_if_interface_init))" << endl << endl;
+
+ // Generate property-related code only for base services---child
+ // service-client classes have only properties inherited from their
+ // parent class
+ if (!extends_service) {
+ // Generate client properties
+ f_service_ << "enum _" << this->nspace << service_name_ << "ClientProperties" << endl << "{"
+ << endl << " PROP_0," << endl << " PROP_" << this->nspace_uc << service_name_uc
+ << "_CLIENT_INPUT_PROTOCOL," << endl << " PROP_" << this->nspace_uc
+ << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL" << endl << "};" << endl << endl;
+
+ // generate property setter
+ f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_set_property ("
+ << "GObject *object, guint property_id, const GValue *value, "
+ << "GParamSpec *pspec)" << endl << "{" << endl << " " << this->nspace
+ << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc
+ << "_CLIENT (object);" << endl << endl << " THRIFT_UNUSED_VAR (pspec);" << endl
+ << endl << " switch (property_id)" << endl << " {" << endl << " case PROP_"
+ << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl
+ << " client->input_protocol = g_value_get_object (value);" << endl
+ << " break;" << endl << " case PROP_" << this->nspace_uc << service_name_uc
+ << "_CLIENT_OUTPUT_PROTOCOL:" << endl
+ << " client->output_protocol = g_value_get_object (value);" << endl
+ << " break;" << endl << " }" << endl << "}" << endl << endl;
+
+ // generate property getter
+ f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_get_property ("
+ << "GObject *object, guint property_id, GValue *value, "
+ << "GParamSpec *pspec)" << endl << "{" << endl << " " << this->nspace
+ << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc
+ << "_CLIENT (object);" << endl << endl << " THRIFT_UNUSED_VAR (pspec);" << endl
+ << endl << " switch (property_id)" << endl << " {" << endl << " case PROP_"
+ << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl
+ << " g_value_set_object (value, client->input_protocol);" << endl
+ << " break;" << endl << " case PROP_" << this->nspace_uc << service_name_uc
+ << "_CLIENT_OUTPUT_PROTOCOL:" << endl
+ << " g_value_set_object (value, client->output_protocol);" << endl
+ << " break;" << endl << " }" << endl << "}" << endl << endl;
+ }
+
+ // Generate client method implementations
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string name = (*f_iter)->get_name();
+ string funname = initial_caps_to_underscores(name);
+
+ // Get the struct of function call params and exceptions
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ // Function for sending
+ t_function send_function(g_type_void,
+ service_name_lc + string("_client_send_") + funname,
+ (*f_iter)->get_arglist());
+
+ // Open the send function
+ indent(f_service_) << function_signature(&send_function) << endl;
+ scope_up(f_service_);
+
+ string reqType = (*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL";
+
+ // Serialize the request
+ f_service_ << indent() << "gint32 cseqid = 0;" << endl << indent()
+ << "ThriftProtocol * protocol = " << this->nspace_uc << base_service_name_uc
+ << "_CLIENT (iface)->output_protocol;" << endl << endl << indent()
+ << "if (thrift_protocol_write_message_begin (protocol, \"" << name << "\", "
+ << reqType << ", cseqid, error) < 0)" << endl << indent() << " return FALSE;"
+ << endl << endl;
+
+ generate_struct_writer(f_service_, arg_struct, "", "", false);
+
+ f_service_ << indent() << "if (thrift_protocol_write_message_end (protocol, error) < 0)" << endl
+ << indent() << " return FALSE;" << endl << indent()
+ << "if (!thrift_transport_flush (protocol->transport, error))" << endl << indent()
+ << " return FALSE;" << endl << indent()
+ << "if (!thrift_transport_write_end (protocol->transport, error))" << endl
+ << indent() << " return FALSE;" << endl << endl << indent() << "return TRUE;"
+ << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate recv function only if not an async function
+ if (!(*f_iter)->is_oneway()) {
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ service_name_lc + string("_client_recv_") + funname,
+ &noargs,
+ (*f_iter)->get_xceptions());
+ // Open function
+ indent(f_service_) << function_signature(&recv_function) << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "gint32 rseqid;" << endl
+ << indent() << "gchar * fname = NULL;" << endl
+ << indent() << "ThriftMessageType mtype;" << endl
+ << indent() << "ThriftProtocol * protocol = "
+ << this->nspace_uc << base_service_name_uc
+ << "_CLIENT (iface)->input_protocol;" << endl
+ << indent() << "ThriftApplicationException *xception;" << endl
+ << endl
+ << indent() << "if (thrift_protocol_read_message_begin "
+ "(protocol, &fname, &mtype, &rseqid, error) < 0) {" << endl;
+ indent_up();
+ f_service_ << indent() << "if (fname) g_free (fname);" << endl
+ << indent() << "return FALSE;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl
+ << endl
+ << indent() << "if (mtype == T_EXCEPTION) {" << endl;
+ indent_up();
+ f_service_ << indent() << "if (fname) g_free (fname);" << endl
+ << indent() << "xception = g_object_new "
+ "(THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);" << endl
+ << indent() << "thrift_struct_read (THRIFT_STRUCT (xception), "
+ "protocol, NULL);" << endl
+ << indent() << "thrift_protocol_read_message_end "
+ "(protocol, NULL);" << endl
+ << indent() << "thrift_transport_read_end "
+ "(protocol->transport, NULL);" << endl
+ << indent() << "g_set_error (error, "
+ "THRIFT_APPLICATION_EXCEPTION_ERROR,xception->type, "
+ "\"application error: %s\", xception->message);" << endl
+ << indent() << "g_object_unref (xception);" << endl
+ << indent() << "return FALSE;" << endl;
+ indent_down();
+ f_service_ << indent() << "} else if (mtype != T_REPLY) {" << endl;
+ indent_up();
+ f_service_ << indent() << "if (fname) g_free (fname);" << endl
+ << indent() << "thrift_protocol_skip (protocol, T_STRUCT, "
+ "NULL);" << endl
+ << indent() << "thrift_protocol_read_message_end (protocol, "
+ "NULL);" << endl
+ << indent() << "thrift_transport_read_end ("
+ "protocol->transport, NULL);" << endl
+ << indent() << "g_set_error (error, "
+ "THRIFT_APPLICATION_EXCEPTION_ERROR, "
+ "THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, "
+ "\"invalid message type %d, expected T_REPLY\", mtype);"
+ << endl
+ << indent() << "return FALSE;" << endl;
+ indent_down();
+ f_service_ << indent() << "} else if (strncmp (fname, \"" << name
+ << "\", " << name.length() << ") != 0) {" << endl;
+ indent_up();
+ f_service_ << indent() << "thrift_protocol_skip (protocol, T_STRUCT, "
+ "NULL);" << endl
+ << indent() << "thrift_protocol_read_message_end (protocol,"
+ "error);" << endl
+ << indent() << "thrift_transport_read_end ("
+ "protocol->transport, error);" << endl
+ << indent() << "g_set_error (error, "
+ "THRIFT_APPLICATION_EXCEPTION_ERROR, "
+ "THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, "
+ "\"wrong method name %s, expected " << name
+ << "\", fname);" << endl
+ << indent() << "if (fname) g_free (fname);" << endl
+ << indent() << "return FALSE;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl
+ << indent() << "if (fname) g_free (fname);" << endl
+ << endl;
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ {
+ t_struct result(program_, tservice->get_name() + "_" + (*f_iter)->get_name() + "_result");
+ t_field success((*f_iter)->get_returntype(), "*_return", 0);
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ // add readers for exceptions, dereferencing the pointer.
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) {
+ t_field* xception = new t_field((*x_iter)->get_type(),
+ "*" + (*x_iter)->get_name(),
+ (*x_iter)->get_key());
+ result.append(xception);
+ }
+
+ generate_struct_reader(f_service_, &result, "", "", false);
+ }
+
+ f_service_ << indent() << "if (thrift_protocol_read_message_end (protocol, error) < 0)"
+ << endl << indent() << " return FALSE;" << endl << endl << indent()
+ << "if (!thrift_transport_read_end (protocol->transport, error))" << endl
+ << indent() << " return FALSE;" << endl << endl;
+
+ // copy over any throw exceptions and return failure
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) {
+ f_service_ << indent() << "if (*" << (*x_iter)->get_name() << " != NULL)" << endl
+ << indent() << "{" << endl << indent() << " g_set_error (error, "
+ << this->nspace_uc
+ << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name()))
+ << "_ERROR, " << this->nspace_uc
+ << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name()))
+ << "_ERROR_CODE, \"" << (*x_iter)->get_type()->get_name() << "\");" << endl
+ << indent() << " return FALSE;" << endl << indent() << "}" << endl;
+ }
+ // Close function
+ indent(f_service_) << "return TRUE;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ // Open function
+ t_function service_function((*f_iter)->get_returntype(),
+ service_name_lc + string("_client_") + funname,
+ (*f_iter)->get_arglist(),
+ (*f_iter)->get_xceptions());
+ indent(f_service_) << function_signature(&service_function) << endl;
+ scope_up(f_service_);
+
+ // wrap each function
+ f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_send_"
+ << funname << " (iface";
+
+ // Declare the function arguments
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << ", " << (*fld_iter)->get_name();
+ }
+ f_service_ << ", error))" << endl << indent() << " return FALSE;" << endl;
+
+ // if not oneway, implement recv
+ if (!(*f_iter)->is_oneway()) {
+ string ret = (*f_iter)->get_returntype()->is_void() ? "" : "_return, ";
+
+ const vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ ret += (*x_iter)->get_name();
+ ret += ", ";
+ }
+
+ f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_recv_"
+ << funname << " (iface, " << ret << "error))" << endl << indent()
+ << " return FALSE;" << endl;
+ }
+
+ // return TRUE which means all functions were called OK
+ indent(f_service_) << "return TRUE;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ // create the interface initializer
+ f_service_ << "static void" << endl
+ << this->nspace_lc << service_name_lc << "_if_interface_init ("
+ << this->nspace << service_name_ << "IfInterface *iface)" << endl;
+ scope_up(f_service_);
+ if (functions.size() > 0) {
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ /* make the function name C friendly */
+ string funname = initial_caps_to_underscores((*f_iter)->get_name());
+
+ f_service_ << indent() << "iface->" << funname << " = " << this->nspace_lc
+ << service_name_lc << "_client_" << funname << ";" << endl;
+ }
+ }
+ else {
+ f_service_ << indent() << "THRIFT_UNUSED_VAR (iface);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // create the client instance initializer
+ f_service_ << "static void" << endl
+ << this->nspace_lc << service_name_lc << "_client_init ("
+ << this->nspace << service_name_ << "Client *client)" << endl;
+ scope_up(f_service_);
+ if (!extends_service) {
+ f_service_ << indent() << "client->input_protocol = NULL;" << endl
+ << indent() << "client->output_protocol = NULL;" << endl;
+ }
+ else {
+ f_service_ << indent() << "THRIFT_UNUSED_VAR (client);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // create the client class initializer
+ f_service_ << "static void" << endl << this->nspace_lc << service_name_lc
+ << "_client_class_init (" << this->nspace << service_name_ << "ClientClass *cls)"
+ << endl << "{" << endl;
+ if (!extends_service) {
+ f_service_ << " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl
+ << " GParamSpec *param_spec;" << endl << endl
+ << " gobject_class->set_property = " << this->nspace_lc << service_name_lc
+ << "_client_set_property;" << endl
+ << " gobject_class->get_property = " << this->nspace_lc << service_name_lc
+ << "_client_get_property;" << endl << endl
+ << " param_spec = g_param_spec_object (\"input_protocol\"," << endl
+ << " \"input protocol (construct)\"," << endl
+ << " \"Set the client input protocol\"," << endl
+ << " THRIFT_TYPE_PROTOCOL," << endl
+ << " G_PARAM_READWRITE);" << endl
+ << " g_object_class_install_property (gobject_class," << endl
+ << " PROP_" << this->nspace_uc << service_name_uc
+ << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl << endl
+ << " param_spec = g_param_spec_object (\"output_protocol\"," << endl
+ << " \"output protocol (construct)\"," << endl
+ << " \"Set the client output protocol\"," << endl
+ << " THRIFT_TYPE_PROTOCOL," << endl
+ << " G_PARAM_READWRITE);" << endl
+ << " g_object_class_install_property (gobject_class," << endl
+ << " PROP_" << this->nspace_uc << service_name_uc
+ << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl;
+ }
+ else {
+ f_service_ << " THRIFT_UNUSED_VAR (cls);" << endl;
+ }
+ f_service_ << "}" << endl << endl;
+}
+
+/**
+ * Generates C code that represents a Thrift service handler.
+ *
+ * @param tservice The service for which to generate a handler.
+ */
+void t_c_glib_generator::generate_service_handler(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator function_iter;
+
+ string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
+ string service_name_uc = to_upper_case(service_name_lc);
+
+ string service_handler_name = service_name_ + "Handler";
+
+ string class_name = this->nspace + service_handler_name;
+ string class_name_lc = this->nspace_lc + initial_caps_to_underscores(service_handler_name);
+ string class_name_uc = to_upper_case(class_name_lc);
+
+ string parent_class_name;
+ string parent_type_name;
+
+ string args_indent;
+
+ // The service this service extends, or NULL if it extends no service
+ t_service* extends_service = tservice->get_extends();
+
+ // Determine the name of our parent service (if any) and the handler class'
+ // parent class name and type
+ if (extends_service) {
+ string parent_service_name = extends_service->get_name();
+ string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name));
+ string parent_service_name_uc = to_upper_case(parent_service_name_lc);
+
+ parent_class_name = this->nspace + parent_service_name + "Handler";
+ parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_HANDLER";
+ } else {
+ parent_class_name = "GObject";
+ parent_type_name = "G_TYPE_OBJECT";
+ }
+
+ // Generate the handler class' definition in the header file
+
+ // Generate the handler instance definition
+ f_header_ << "/* " << service_name_ << " handler (abstract base class) */" << endl << "struct _"
+ << class_name << endl << "{" << endl;
+ indent_up();
+ f_header_ << indent() << parent_class_name << " parent;" << endl;
+ indent_down();
+ f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl
+ << endl;
+
+ // Generate the handler class definition, including its class members
+ // (methods)
+ f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl;
+ indent_up();
+ f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl;
+
+ for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
+ string method_name = initial_caps_to_underscores((*function_iter)->get_name());
+ t_type* return_type = (*function_iter)->get_returntype();
+ t_struct* arg_list = (*function_iter)->get_arglist();
+ t_struct* x_list = (*function_iter)->get_xceptions();
+ bool has_return = !return_type->is_void();
+ bool has_args = arg_list->get_members().size() == 0;
+ bool has_xceptions = x_list->get_members().size() == 0;
+
+ string params = "(" + this->nspace + service_name_ + "If *iface"
+ + (has_return ? ", " + type_name(return_type) + "* _return" : "")
+ + (has_args ? "" : (", " + argument_list(arg_list)))
+ + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)";
+
+ indent(f_header_) << "gboolean (*" << method_name << ") " << params << ";" << endl;
+ }
+ indent_down();
+
+ f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name
+ << "Class;" << endl << endl;
+
+ // Generate the remaining header boilerplate
+ f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define "
+ << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER "
+ << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc
+ << "(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_HANDLER, " << class_name << "))" << endl << "#define "
+ << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_HANDLER))" << endl << "#define " << class_name_uc
+ << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << "#define "
+ << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER_CLASS(c) "
+ << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
+ << "_HANDLER))" << endl << "#define " << this->nspace_uc << service_name_uc
+ << "_HANDLER_GET_CLASS(obj) "
+ << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << endl;
+
+ // Generate the handler class' method definitions
+ for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
+ string method_name = initial_caps_to_underscores((*function_iter)->get_name());
+ t_type* return_type = (*function_iter)->get_returntype();
+ t_struct* arg_list = (*function_iter)->get_arglist();
+ t_struct* x_list = (*function_iter)->get_xceptions();
+ bool has_return = !return_type->is_void();
+ bool has_args = arg_list->get_members().size() == 0;
+ bool has_xceptions = x_list->get_members().size() == 0;
+
+ string params = "(" + this->nspace + service_name_ + "If *iface"
+ + (has_return ? ", " + type_name(return_type) + "* _return" : "")
+ + (has_args ? "" : (", " + argument_list(arg_list)))
+ + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)";
+
+ f_header_ << "gboolean " << class_name_lc << "_" << method_name << " " << params << ";" << endl;
+ }
+ f_header_ << endl;
+
+ // Generate the handler's implementation in the implementation file
+
+ // Generate the implementation boilerplate
+ f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc
+ << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);"
+ << endl << endl;
+
+ args_indent = string(25, ' ');
+ f_service_ << "G_DEFINE_TYPE_WITH_CODE (" << class_name << ", " << endl << args_indent
+ << class_name_lc << "," << endl << args_indent << parent_type_name << "," << endl
+ << args_indent << "G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_IF," << endl;
+ args_indent += string(23, ' ');
+ f_service_ << args_indent << class_name_lc << "_" << service_name_lc << "_if_interface_init))"
+ << endl << endl;
+
+ // Generate the handler method implementations
+ for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
+ string function_name = (*function_iter)->get_name();
+ string method_name = initial_caps_to_underscores(function_name);
+ t_type* return_type = (*function_iter)->get_returntype();
+ t_struct* arg_list = (*function_iter)->get_arglist();
+ t_struct* x_list = (*function_iter)->get_xceptions();
+
+ const vector<t_field*>& args = arg_list->get_members();
+ const vector<t_field*>& xceptions = x_list->get_members();
+
+ vector<t_field*>::const_iterator field_iter;
+
+ t_function implementing_function(return_type,
+ service_name_lc + "_handler_" + method_name,
+ arg_list,
+ x_list,
+ (*function_iter)->is_oneway());
+
+ indent(f_service_) << function_signature(&implementing_function) << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "g_return_val_if_fail (" << this->nspace_uc << "IS_"
+ << service_name_uc << "_HANDLER (iface), FALSE);" << endl << endl << indent()
+ << "return " << class_name_uc << "_GET_CLASS (iface)"
+ << "->" << method_name << " (iface, ";
+
+ if (!return_type->is_void()) {
+ f_service_ << "_return, ";
+ }
+ for (field_iter = args.begin(); field_iter != args.end(); ++field_iter) {
+ f_service_ << (*field_iter)->get_name() << ", ";
+ }
+ for (field_iter = xceptions.begin(); field_iter != xceptions.end(); ++field_iter) {
+ f_service_ << (*field_iter)->get_name() << ", ";
+ }
+ f_service_ << "error);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ // Generate the handler interface initializer
+ f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc
+ << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface)"
+ << endl;
+ scope_up(f_service_);
+ if (functions.size() > 0) {
+ for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
+ string method_name = initial_caps_to_underscores((*function_iter)->get_name());
+
+ f_service_ << indent() << "iface->" << method_name << " = " << class_name_lc << "_"
+ << method_name << ";" << endl;
+ }
+ }
+ else {
+ f_service_ << "THRIFT_UNUSED_VAR (iface);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the handler instance initializer
+ f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)"
+ << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "THRIFT_UNUSED_VAR (self);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the handler class initializer
+ f_service_ << "static void" << endl
+ << class_name_lc << "_class_init (" << class_name << "Class *cls)"
+ << endl;
+ scope_up(f_service_);
+ if (functions.size() > 0) {
+ for (function_iter = functions.begin();
+ function_iter != functions.end();
+ ++function_iter) {
+ string function_name = (*function_iter)->get_name();
+ string method_name = initial_caps_to_underscores(function_name);
+
+ // All methods are pure virtual and must be implemented by subclasses
+ f_service_ << indent() << "cls->" << method_name << " = NULL;" << endl;
+ }
+ }
+ else {
+ f_service_ << indent() << "THRIFT_UNUSED_VAR (cls);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+}
+
+/**
+ * Generates C code that represents a Thrift service processor.
+ *
+ * @param tservice The service for which to generate a processor
+ */
+void t_c_glib_generator::generate_service_processor(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator function_iter;
+
+ string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
+ string service_name_uc = to_upper_case(service_name_lc);
+
+ string service_processor_name = service_name_ + "Processor";
+
+ string class_name = this->nspace + service_processor_name;
+ string class_name_lc = this->nspace_lc + initial_caps_to_underscores(service_processor_name);
+ string class_name_uc = to_upper_case(class_name_lc);
+
+ string parent_class_name;
+ string parent_type_name;
+
+ string handler_class_name = this->nspace + service_name_ + "Handler";
+ string handler_class_name_lc = initial_caps_to_underscores(handler_class_name);
+
+ string process_function_type_name = class_name + "ProcessFunction";
+ string process_function_def_type_name =
+ class_name_lc + "_process_function_def";
+
+ string function_name;
+ string args_indent;
+
+ // The service this service extends, or NULL if it extends no service
+ t_service* extends_service = tservice->get_extends();
+
+ // Determine the name of our parent service (if any) and the
+ // processor class' parent class name and type
+ if (extends_service) {
+ string parent_service_name = extends_service->get_name();
+ string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name));
+ string parent_service_name_uc = to_upper_case(parent_service_name_lc);
+
+ parent_class_name = this->nspace + parent_service_name + "Processor";
+ parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_PROCESSOR";
+ } else {
+ parent_class_name = "ThriftDispatchProcessor";
+ parent_type_name = "THRIFT_TYPE_DISPATCH_PROCESSOR";
+ }
+
+ // Generate the processor class' definition in the header file
+
+ // Generate the processor instance definition
+ f_header_ << "/* " << service_name_ << " processor */" << endl << "struct _" << class_name << endl
+ << "{" << endl;
+ indent_up();
+ f_header_ << indent() << parent_class_name << " parent;" << endl << endl << indent()
+ << "/* protected */" << endl << indent()
+ << this->nspace + service_name_ + "Handler *handler;" << endl << indent()
+ << "GHashTable *process_map;" << endl;
+ indent_down();
+ f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl
+ << endl;
+
+ // Generate the processor class definition
+ f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl;
+ indent_up();
+ f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl << indent()
+ << "/* protected */" << endl << indent()
+ << "gboolean (*dispatch_call) (ThriftDispatchProcessor *processor," << endl;
+ args_indent = indent() + string(27, ' ');
+ f_header_ << args_indent << "ThriftProtocol *in," << endl << args_indent << "ThriftProtocol *out,"
+ << endl << args_indent << "gchar *fname," << endl << args_indent << "gint32 seqid,"
+ << endl << args_indent << "GError **error);" << endl;
+ indent_down();
+ f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name
+ << "Class;" << endl << endl;
+
+ // Generate the remaining header boilerplate
+ f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define "
+ << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR "
+ << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc
+ << "(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_PROCESSOR, " << class_name << "))" << endl << "#define "
+ << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR(obj) "
+ << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_PROCESSOR))" << endl << "#define " << class_name_uc
+ << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << "#define "
+ << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR_CLASS(c) "
+ << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc
+ << "_PROCESSOR))" << endl << "#define " << this->nspace_uc << service_name_uc
+ << "_PROCESSOR_GET_CLASS(obj) "
+ << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
+ << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << endl;
+
+ // Generate the processor's implementation in the implementation file
+
+ // Generate the processor's properties enum
+ f_service_ << "enum _" << class_name << "Properties" << endl << "{" << endl;
+ indent_up();
+ f_service_ << indent() << "PROP_" << class_name_uc << "_0," << endl << indent() << "PROP_"
+ << class_name_uc << "_HANDLER" << endl;
+ indent_down();
+ f_service_ << "};" << endl << endl;
+
+ // Generate the implementation boilerplate
+ args_indent = string(15, ' ');
+ f_service_ << "G_DEFINE_TYPE (" << class_name << "," << endl << args_indent << class_name_lc
+ << "," << endl << args_indent << parent_type_name << ")" << endl << endl;
+
+ // Generate the processor's processing-function type
+ args_indent = string(process_function_type_name.length() + 23, ' ');
+ f_service_ << "typedef gboolean (* " << process_function_type_name << ") ("
+ << class_name << " *, " << endl
+ << args_indent << "gint32," << endl
+ << args_indent << "ThriftProtocol *," << endl
+ << args_indent << "ThriftProtocol *," << endl
+ << args_indent << "GError **);" << endl
+ << endl;
+
+ // Generate the processor's processing-function-definition type
+ f_service_ << "typedef struct" << endl
+ << "{" << endl;
+ indent_up();
+ f_service_ << indent() << "gchar *name;" << endl
+ << indent() << process_function_type_name << " function;" << endl;
+ indent_down();
+ f_service_ << "} " << process_function_def_type_name << ";" << endl
+ << endl;
+
+ // Generate forward declarations of the processor's processing functions so we
+ // can refer to them in the processing-function-definition struct below and
+ // keep all of the processor's declarations in one place
+ for (function_iter = functions.begin();
+ function_iter != functions.end();
+ ++function_iter) {
+ function_name = class_name_lc + "_process_"
+ + initial_caps_to_underscores((*function_iter)->get_name());
+
+ args_indent = string(function_name.length() + 2, ' ');
+ f_service_ << "static gboolean" << endl
+ << function_name << " ("
+ << class_name << " *," << endl
+ << args_indent << "gint32," << endl
+ << args_indent << "ThriftProtocol *," << endl
+ << args_indent << "ThriftProtocol *," << endl
+ << args_indent << "GError **);" << endl;
+ }
+ f_service_ << endl;
+
+ // Generate the processor's processing-function definitions, if the service
+ // defines any methods
+ if (functions.size() > 0) {
+ f_service_ << indent() << "static " << process_function_def_type_name
+ << endl
+ << indent() << class_name_lc << "_process_function_defs["
+ << functions.size() << "] = {" << endl;
+ indent_up();
+ for (function_iter = functions.begin();
+ function_iter != functions.end();
+ ++function_iter) {
+ string service_function_name = (*function_iter)->get_name();
+ string process_function_name = class_name_lc + "_process_"
+ + initial_caps_to_underscores(service_function_name);
+
+ f_service_ << indent() << "{" << endl;
+ indent_up();
+ f_service_ << indent() << "\"" << service_function_name << "\"," << endl
+ << indent() << process_function_name << endl;
+ indent_down();
+ f_service_ << indent() << "}"
+ << (function_iter == --functions.end() ? "" : ",") << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "};" << endl
+ << endl;
+ }
+
+ // Generate the processor's processing functions
+ for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) {
+ string service_function_name = (*function_iter)->get_name();
+ string service_function_name_ic = underscores_to_initial_caps(service_function_name);
+ string service_function_name_lc = initial_caps_to_underscores(service_function_name);
+ string service_function_name_uc = to_upper_case(service_function_name_lc);
+
+ t_type* return_type = (*function_iter)->get_returntype();
+ bool has_return_value = !return_type->is_void();
+
+ t_struct* arg_list = (*function_iter)->get_arglist();
+ const vector<t_field*>& args = arg_list->get_members();
+ vector<t_field*>::const_iterator arg_iter;
+
+ const vector<t_field*>& xceptions = (*function_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator xception_iter;
+
+ string args_class_name = this->nspace + service_name_ + service_function_name_ic + "Args";
+ string args_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_"
+ + service_function_name_uc + "_ARGS";
+
+ string result_class_name = this->nspace + service_name_ + service_function_name_ic + "Result";
+ string result_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_"
+ + service_function_name_uc + "_RESULT";
+
+ string handler_function_name = handler_class_name_lc + "_" + service_function_name_lc;
+
+ function_name = class_name_lc + "_process_"
+ + initial_caps_to_underscores(service_function_name);
+
+ args_indent = string(function_name.length() + 2, ' ');
+ f_service_ << "static gboolean" << endl << function_name << " (" << class_name << " *self,"
+ << endl << args_indent << "gint32 sequence_id," << endl << args_indent
+ << "ThriftProtocol *input_protocol," << endl << args_indent
+ << "ThriftProtocol *output_protocol," << endl << args_indent << "GError **error)"
+ << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "gboolean result = TRUE;" << endl
+ << indent() << "ThriftTransport * transport;" << endl
+ << indent() << "ThriftApplicationException *xception;" << endl
+ << indent() << args_class_name + " * args =" << endl;
+ indent_up();
+ f_service_ << indent() << "g_object_new (" << args_class_type << ", NULL);" << endl << endl;
+ indent_down();
+ if ((*function_iter)->is_oneway()) {
+ f_service_ << indent() << "THRIFT_UNUSED_VAR (sequence_id);" << endl << indent()
+ << "THRIFT_UNUSED_VAR (output_protocol);" << endl << endl;
+ }
+ f_service_ << indent() << "g_object_get (input_protocol, \"transport\", "
+ << "&transport, NULL);" << endl << endl;
+
+ // Read the method's arguments from the caller
+ f_service_ << indent() << "if ((thrift_struct_read (THRIFT_STRUCT (args), "
+ << "input_protocol, error) != -1) &&" << endl << indent()
+ << " (thrift_protocol_read_message_end (input_protocol, "
+ << "error) != -1) &&" << endl << indent()
+ << " (thrift_transport_read_end (transport, error) != FALSE))" << endl;
+ scope_up(f_service_);
+
+ for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
+ f_service_ << indent() << property_type_name((*arg_iter)->get_type()) << " "
+ << (*arg_iter)->get_name() << ";" << endl;
+ }
+ for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) {
+ f_service_ << indent() << type_name((*xception_iter)->get_type()) << " "
+ << initial_caps_to_underscores((*xception_iter)->get_name()) << " = NULL;" << endl;
+ }
+ if (has_return_value) {
+ f_service_ << indent() << property_type_name(return_type) << " return_value;" << endl;
+ }
+ if (!(*function_iter)->is_oneway()) {
+ f_service_ << indent() << result_class_name << " * result_struct;" << endl;
+ }
+ f_service_ << endl;
+
+ if (args.size() > 0) {
+ f_service_ << indent() << "g_object_get (args," << endl;
+ args_indent = indent() + string(14, ' ');
+ for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
+ string arg_name = (*arg_iter)->get_name();
+
+ f_service_ << args_indent << "\"" << arg_name << "\", &" << arg_name << "," << endl;
+ }
+ f_service_ << args_indent << "NULL);" << endl << endl;
+ }
+
+ if (!(*function_iter)->is_oneway()) {
+ f_service_ << indent() << "g_object_unref (transport);" << endl << indent()
+ << "g_object_get (output_protocol, \"transport\", "
+ << "&transport, NULL);" << endl << endl << indent()
+ << "result_struct = g_object_new (" << result_class_type << ", NULL);" << endl;
+ if (has_return_value) {
+ f_service_ << indent() << "g_object_get (result_struct, "
+ "\"success\", &return_value, NULL);" << endl;
+ }
+ f_service_ << endl;
+ }
+
+ // Pass the arguments to the corresponding method in the handler
+ f_service_ << indent() << "if (" << handler_function_name << " (" << this->nspace_uc
+ << service_name_uc << "_IF (self->handler)," << endl;
+ args_indent = indent() + string(handler_function_name.length() + 6, ' ');
+ if (has_return_value) {
+ string return_type_name = type_name(return_type);
+
+ f_service_ << args_indent;
+
+ // Cast return_value if it was declared as a type other than the return
+ // value's actual type---this is true for integer values 32 bits or fewer
+ // in width, for which GLib requires a plain gint type be used when
+ // storing or retrieving as an object property
+ if (return_type_name != property_type_name(return_type)) {
+ if (return_type_name[return_type_name.length() - 1] != '*') {
+ return_type_name += ' ';
+ }
+ return_type_name += '*';
+
+ f_service_ << "(" << return_type_name << ")";
+ }
+
+ f_service_ << "&return_value," << endl;
+ }
+ for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
+ f_service_ << args_indent << (*arg_iter)->get_name() << "," << endl;
+ }
+ for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) {
+ f_service_ << args_indent << "&" << initial_caps_to_underscores((*xception_iter)->get_name())
+ << "," << endl;
+ }
+ f_service_ << args_indent << "error) == TRUE)" << endl;
+ scope_up(f_service_);
+
+ // The handler reported success; return the result, if any, to the caller
+ if (!(*function_iter)->is_oneway()) {
+ if (has_return_value) {
+ f_service_ << indent() << "g_object_set (result_struct, \"success\", ";
+ if (type_name(return_type) != property_type_name(return_type)) {
+ // Roundtrip cast to fix the position of sign bit.
+ f_service_ << "(" << property_type_name(return_type) << ")"
+ << "(" << type_name(return_type) << ")";
+ }
+ f_service_ << "return_value, "
+ << "NULL);" << endl;
+
+ // Deallocate (or unref) return_value
+ return_type = get_true_type(return_type);
+ if (return_type->is_base_type()) {
+ t_base_type* base_type = ((t_base_type*)return_type);
+
+ if (base_type->get_base() == t_base_type::TYPE_STRING) {
+ f_service_ << indent() << "if (return_value != NULL)" << endl;
+ indent_up();
+ if (base_type->is_binary()) {
+ f_service_ << indent() << "g_byte_array_unref (return_value);" << endl;
+ } else {
+ f_service_ << indent() << "g_free (return_value);" << endl;
+ }
+ indent_down();
+ }
+ } else if (return_type->is_container()) {
+ f_service_ << indent() << "if (return_value != NULL)" << endl;
+ indent_up();
+
+ if (return_type->is_list()) {
+ t_type* elem_type = ((t_list*)return_type)->get_elem_type();
+
+ f_service_ << indent();
+ if (is_numeric(elem_type)) {
+ f_service_ << "g_array_unref";
+ } else {
+ f_service_ << "g_ptr_array_unref";
+ }
+ f_service_ << " (return_value);" << endl;
+ } else if (return_type->is_map() || return_type->is_set()) {
+ f_service_ << indent() << "g_hash_table_unref (return_value);" << endl;
+ }
+
+ indent_down();
+ } else if (return_type->is_struct()) {
+ f_service_ << indent() << "if (return_value != NULL)" << endl;
+ indent_up();
+ f_service_ << indent() << "g_object_unref (return_value);" << endl;
+ indent_down();
+ }
+
+ f_service_ << endl;
+ }
+ f_service_ << indent() << "result =" << endl;
+ indent_up();
+ f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl;
+ args_indent = indent() + string(39, ' ');
+ f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent
+ << "T_REPLY," << endl << args_indent << "sequence_id," << endl << args_indent
+ << "error) != -1) &&" << endl << indent()
+ << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << endl;
+ args_indent = indent() + string(23, ' ');
+ f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));"
+ << endl;
+ indent_down();
+ }
+ scope_down(f_service_);
+ f_service_ << indent() << "else" << endl;
+ scope_up(f_service_);
+
+ // The handler reported failure; check to see if an application-defined
+ // exception was raised and if so, return it to the caller
+ f_service_ << indent();
+ if (xceptions.size() > 0) {
+ for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) {
+ f_service_ << "if (" << initial_caps_to_underscores((*xception_iter)->get_name())
+ << " != NULL)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "g_object_set (result_struct," << endl;
+ args_indent = indent() + string(14, ' ');
+ f_service_ << args_indent << "\"" << (*xception_iter)->get_name() << "\", "
+ << (*xception_iter)->get_name() << "," << endl << args_indent << "NULL);" << endl
+ << endl;
+ f_service_ << indent() << "result =" << endl;
+ indent_up();
+ f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl;
+ args_indent = indent() + string(39, ' ');
+ f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent
+ << "T_REPLY," << endl << args_indent << "sequence_id," << endl << args_indent
+ << "error) != -1) &&" << endl << indent()
+ << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << endl;
+ args_indent = indent() + string(23, ' ');
+ f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));"
+ << endl;
+ indent_down();
+ scope_down(f_service_);
+ f_service_ << indent() << "else" << endl;
+ }
+
+ scope_up(f_service_);
+ f_service_ << indent();
+ }
+
+ // If the handler reported failure but raised no application-defined
+ // exception, return a Thrift application exception with the information
+ // returned via GLib's own error-reporting mechanism
+ f_service_ << "if (*error == NULL)" << endl;
+ indent_up();
+ f_service_ << indent() << "g_warning (\"" << service_name_ << "."
+ << (*function_iter)->get_name() << " implementation returned FALSE \"" << endl
+ << indent() << string(11, ' ') << "\"but did not set an error\");" << endl << endl;
+ indent_down();
+ f_service_ << indent() << "xception =" << endl;
+ indent_up();
+ f_service_ << indent() << "g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION," << endl;
+ args_indent = indent() + string(14, ' ');
+ f_service_ << args_indent << "\"type\", *error != NULL ? (*error)->code :" << endl
+ << args_indent << string(11, ' ') << "THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN,"
+ << endl << args_indent << "\"message\", *error != NULL ? (*error)->message : NULL,"
+ << endl << args_indent << "NULL);" << endl;
+ indent_down();
+ f_service_ << indent() << "g_clear_error (error);" << endl << endl << indent()
+ << "result =" << endl;
+ indent_up();
+ f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl;
+ args_indent = indent() + string(39, ' ');
+ f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent
+ << "T_EXCEPTION," << endl << args_indent << "sequence_id," << endl << args_indent
+ << "error) != -1) &&" << endl << indent()
+ << " (thrift_struct_write (THRIFT_STRUCT (xception)," << endl;
+ args_indent = indent() + string(23, ' ');
+ f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));"
+ << endl;
+ indent_down();
+ f_service_ << endl << indent() << "g_object_unref (xception);" << endl;
+
+ if (xceptions.size() > 0) {
+ scope_down(f_service_);
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Dellocate or unref retrieved argument values as necessary
+ for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) {
+ string arg_name = (*arg_iter)->get_name();
+ t_type* arg_type = get_true_type((*arg_iter)->get_type());
+
+ if (arg_type->is_base_type()) {
+ t_base_type* base_type = ((t_base_type*)arg_type);
+
+ if (base_type->get_base() == t_base_type::TYPE_STRING) {
+ f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl;
+ indent_up();
+ if (base_type->is_binary()) {
+ f_service_ << indent() << "g_byte_array_unref (" << arg_name << ");" << endl;
+ } else {
+ f_service_ << indent() << "g_free (" << arg_name << ");" << endl;
+ }
+ indent_down();
+ }
+ } else if (arg_type->is_container()) {
+ f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl;
+ indent_up();
+
+ if (arg_type->is_list()) {
+ t_type* elem_type = ((t_list*)arg_type)->get_elem_type();
+
+ f_service_ << indent();
+ if (is_numeric(elem_type)) {
+ f_service_ << "g_array_unref";
+ } else {
+ f_service_ << "g_ptr_array_unref";
+ }
+ f_service_ << " (" << arg_name << ");" << endl;
+ } else if (arg_type->is_map() || arg_type->is_set()) {
+ f_service_ << indent() << "g_hash_table_unref (" << arg_name << ");" << endl;
+ }
+
+ indent_down();
+ } else if (arg_type->is_struct()) {
+ f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl;
+ indent_up();
+ f_service_ << indent() << "g_object_unref (" << arg_name << ");" << endl;
+ indent_down();
+ }
+ }
+
+ if (!(*function_iter)->is_oneway()) {
+ f_service_ << indent() << "g_object_unref (result_struct);" << endl << endl << indent()
+ << "if (result == TRUE)" << endl;
+ indent_up();
+ f_service_ << indent() << "result =" << endl;
+ indent_up();
+ f_service_ << indent() << "((thrift_protocol_write_message_end "
+ << "(output_protocol, error) != -1) &&" << endl << indent()
+ << " (thrift_transport_write_end (transport, error) "
+ << "!= FALSE) &&" << endl << indent()
+ << " (thrift_transport_flush (transport, error) "
+ << "!= FALSE));" << endl;
+ indent_down();
+ indent_down();
+ }
+ scope_down(f_service_);
+ f_service_ << indent() << "else" << endl;
+ indent_up();
+ f_service_ << indent() << "result = FALSE;" << endl;
+ indent_down();
+
+ f_service_ << endl << indent() << "g_object_unref (transport);" << endl << indent()
+ << "g_object_unref (args);" << endl << endl << indent() << "return result;" << endl;
+ scope_down(f_service_);
+
+ f_service_ << endl;
+ }
+
+ // Generate the processor's dispatch_call implementation
+ function_name = class_name_lc + "_dispatch_call";
+ args_indent = indent() + string(function_name.length() + 2, ' ');
+ f_service_ << "static gboolean" << endl << function_name
+ << " (ThriftDispatchProcessor *dispatch_processor," << endl << args_indent
+ << "ThriftProtocol *input_protocol," << endl << args_indent
+ << "ThriftProtocol *output_protocol," << endl << args_indent << "gchar *method_name,"
+ << endl << args_indent << "gint32 sequence_id," << endl << args_indent
+ << "GError **error)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << class_name_lc << "_process_function_def *"
+ << "process_function_def;" << endl;
+ f_service_ << indent() << "gboolean dispatch_result = FALSE;" << endl << endl << indent()
+ << class_name << " *self = " << class_name_uc << " (dispatch_processor);" << endl;
+ f_service_ << indent() << parent_class_name << "Class "
+ "*parent_class =" << endl;
+ indent_up();
+ f_service_ << indent() << "g_type_class_peek_parent (" << class_name_uc << "_GET_CLASS (self));"
+ << endl;
+ indent_down();
+ f_service_ << endl
+ << indent() << "process_function_def = "
+ << "g_hash_table_lookup (self->process_map, method_name);" << endl
+ << indent() << "if (process_function_def != NULL)" << endl;
+ scope_up(f_service_);
+ args_indent = indent() + string(53, ' ');
+ f_service_ << indent() << "g_free (method_name);" << endl
+ << indent() << "dispatch_result = "
+ << "(*process_function_def->function) (self," << endl
+ << args_indent << "sequence_id," << endl
+ << args_indent << "input_protocol," << endl
+ << args_indent << "output_protocol," << endl
+ << args_indent << "error);" << endl;
+ scope_down(f_service_);
+ f_service_ << indent() << "else" << endl;
+ scope_up(f_service_);
+
+ // Method name not recognized; chain up to our parent processor---note the
+ // top-most implementation of this method, in ThriftDispatchProcessor itself,
+ // will return an application exception to the caller if no class in the
+ // hierarchy recognizes the method name
+ f_service_ << indent() << "dispatch_result = parent_class->dispatch_call "
+ "(dispatch_processor," << endl;
+ args_indent = indent() + string(47, ' ');
+ f_service_ << args_indent << "input_protocol," << endl << args_indent << "output_protocol,"
+ << endl << args_indent << "method_name," << endl << args_indent << "sequence_id,"
+ << endl << args_indent << "error);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl << indent() << "return dispatch_result;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the processor's property setter
+ function_name = class_name_lc + "_set_property";
+ args_indent = string(function_name.length() + 2, ' ');
+ f_service_ << "static void" << endl << function_name << " (GObject *object," << endl
+ << args_indent << "guint property_id," << endl << args_indent << "const GValue *value,"
+ << endl << args_indent << "GParamSpec *pspec)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
+ << endl << indent() << "switch (property_id)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << endl;
+ indent_up();
+ f_service_ << indent() << "if (self->handler != NULL)" << endl;
+ indent_up();
+ f_service_ << indent() << "g_object_unref (self->handler);" << endl;
+ indent_down();
+ f_service_ << indent() << "self->handler = g_value_get_object (value);" << endl << indent()
+ << "g_object_ref (self->handler);" << endl;
+ if (extends_service) {
+ // Chain up to set the handler in every superclass as well
+ f_service_ << endl << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)->"
+ << endl;
+ indent_up();
+ f_service_ << indent() << "set_property (object, property_id, value, pspec);" << endl;
+ indent_down();
+ }
+ f_service_ << indent() << "break;" << endl;
+ indent_down();
+ f_service_ << indent() << "default:" << endl;
+ indent_up();
+ f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
+ << endl << indent() << "break;" << endl;
+ indent_down();
+ scope_down(f_service_);
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate processor's property getter
+ function_name = class_name_lc + "_get_property";
+ args_indent = string(function_name.length() + 2, ' ');
+ f_service_ << "static void" << endl << function_name << " (GObject *object," << endl
+ << args_indent << "guint property_id," << endl << args_indent << "GValue *value,"
+ << endl << args_indent << "GParamSpec *pspec)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
+ << endl << indent() << "switch (property_id)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << endl;
+ indent_up();
+ f_service_ << indent() << "g_value_set_object (value, self->handler);" << endl << indent()
+ << "break;" << endl;
+ indent_down();
+ f_service_ << indent() << "default:" << endl;
+ indent_up();
+ f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
+ << endl << indent() << "break;" << endl;
+ indent_down();
+ scope_down(f_service_);
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generator the processor's dispose function
+ f_service_ << "static void" << endl << class_name_lc << "_dispose (GObject *gobject)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << class_name << " *self = " << class_name_uc << " (gobject);" << endl
+ << endl << indent() << "if (self->handler != NULL)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "g_object_unref (self->handler);" << endl << indent()
+ << "self->handler = NULL;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)"
+ "->dispose (gobject);"
+ << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate processor finalize function
+ f_service_ << "static void" << endl << class_name_lc << "_finalize (GObject *gobject)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << this->nspace << service_name_ << "Processor *self = " << this->nspace_uc
+ << service_name_uc << "_PROCESSOR (gobject);" << endl << endl << indent()
+ << "thrift_safe_hash_table_destroy (self->process_map);" << endl << endl << indent()
+ << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)"
+ "->finalize (gobject);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate processor instance initializer
+ f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)"
+ << endl;
+ scope_up(f_service_);
+ if (functions.size() > 0) {
+ f_service_ << indent() << "guint index;" << endl
+ << endl;
+ }
+ f_service_ << indent() << "self->handler = NULL;" << endl << indent()
+ << "self->process_map = "
+ "g_hash_table_new (g_str_hash, g_str_equal);" << endl;
+ if (functions.size() > 0) {
+ args_indent = string(21, ' ');
+ f_service_ << endl
+ << indent() << "for (index = 0; index < "
+ << functions.size() << "; index += 1)" << endl;
+ indent_up();
+ f_service_ << indent() << "g_hash_table_insert (self->process_map," << endl
+ << indent() << args_indent
+ << class_name_lc << "_process_function_defs[index].name," << endl
+ << indent() << args_indent
+ << "&" << class_name_lc << "_process_function_defs[index]" << ");"
+ << endl;
+ indent_down();
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate processor class initializer
+ f_service_ << "static void" << endl << class_name_lc << "_class_init (" << class_name
+ << "Class *cls)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl
+ << indent() << "ThriftDispatchProcessorClass *dispatch_processor_class =" << endl;
+ indent_up();
+ f_service_ << indent() << "THRIFT_DISPATCH_PROCESSOR_CLASS (cls);" << endl;
+ indent_down();
+ f_service_ << indent() << "GParamSpec *param_spec;" << endl << endl << indent()
+ << "gobject_class->dispose = " << class_name_lc << "_dispose;" << endl << indent()
+ << "gobject_class->finalize = " << class_name_lc << "_finalize;" << endl << indent()
+ << "gobject_class->set_property = " << class_name_lc << "_set_property;" << endl
+ << indent() << "gobject_class->get_property = " << class_name_lc << "_get_property;"
+ << endl << endl << indent()
+ << "dispatch_processor_class->dispatch_call = " << class_name_lc << "_dispatch_call;"
+ << endl << indent() << "cls->dispatch_call = " << class_name_lc << "_dispatch_call;"
+ << endl << endl << indent() << "param_spec = g_param_spec_object (\"handler\","
+ << endl;
+ args_indent = indent() + string(34, ' ');
+ f_service_ << args_indent << "\"Service handler implementation\"," << endl << args_indent
+ << "\"The service handler implementation \"" << endl << args_indent
+ << "\"to which method calls are dispatched.\"," << endl << args_indent
+ << this->nspace_uc + "TYPE_" + service_name_uc + "_HANDLER," << endl << args_indent
+ << "G_PARAM_READWRITE);" << endl;
+ f_service_ << indent() << "g_object_class_install_property (gobject_class," << endl;
+ args_indent = indent() + string(33, ' ');
+ f_service_ << args_indent << "PROP_" << class_name_uc << "_HANDLER," << endl << args_indent
+ << "param_spec);" << endl;
+ scope_down(f_service_);
+}
+
+/**
+ * Generates C code that represents a Thrift service server.
+ */
+void t_c_glib_generator::generate_service_server(t_service* tservice) {
+ (void)tservice;
+ // Generate the service's handler class
+ generate_service_handler(tservice);
+
+ // Generate the service's processor class
+ generate_service_processor(tservice);
+}
+
+/**
+ * Generates C code to represent a THrift structure as a GObject.
+ */
+void t_c_glib_generator::generate_object(t_struct* tstruct) {
+ string name = tstruct->get_name();
+ string name_u = initial_caps_to_underscores(name);
+ string name_uc = to_upper_case(name_u);
+
+ string class_name = this->nspace + name;
+ string class_name_lc = this->nspace_lc + initial_caps_to_underscores(name);
+ string class_name_uc = to_upper_case(class_name_lc);
+
+ string function_name;
+ string args_indent;
+
+ // write the instance definition
+ f_types_ << "struct _" << this->nspace << name << endl << "{ " << endl
+ << " ThriftStruct parent; " << endl << endl << " /* public */" << endl;
+
+ // for each field, add a member variable
+ vector<t_field*>::const_iterator m_iter;
+ const vector<t_field*>& members = tstruct->get_members();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ f_types_ << " " << type_name(t) << " " << (*m_iter)->get_name() << ";" << endl;
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ f_types_ << " gboolean __isset_" << (*m_iter)->get_name() << ";" << endl;
+ }
+ }
+
+ // close the structure definition and create a typedef
+ f_types_ << "};" << endl << "typedef struct _" << this->nspace << name << " " << this->nspace
+ << name << ";" << endl << endl;
+
+ // write the class definition
+ f_types_ << "struct _" << this->nspace << name << "Class" << endl << "{" << endl
+ << " ThriftStructClass parent;" << endl << "};" << endl << "typedef struct _"
+ << this->nspace << name << "Class " << this->nspace << name << "Class;" << endl << endl;
+
+ // write the standard GObject boilerplate
+ f_types_ << "GType " << this->nspace_lc << name_u << "_get_type (void);" << endl << "#define "
+ << this->nspace_uc << "TYPE_" << name_uc << " (" << this->nspace_lc << name_u
+ << "_get_type())" << endl << "#define " << this->nspace_uc << name_uc
+ << "(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << name_uc
+ << ", " << this->nspace << name << "))" << endl << "#define " << this->nspace_uc
+ << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "_TYPE_"
+ << name_uc << ", " << this->nspace << name << "Class))" << endl << "#define "
+ << this->nspace_uc << "IS_" << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), "
+ << this->nspace_uc << "TYPE_" << name_uc << "))" << endl << "#define " << this->nspace_uc
+ << "IS_" << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc
+ << "TYPE_" << name_uc << "))" << endl << "#define " << this->nspace_uc << name_uc
+ << "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
+ << name_uc << ", " << this->nspace << name << "Class))" << endl << endl;
+
+ // start writing the object implementation .c file
+
+ // generate properties enum
+ if (members.size() > 0) {
+ f_types_impl_ << "enum _" << class_name << "Properties" << endl << "{" << endl;
+ indent_up();
+ f_types_impl_ << indent() << "PROP_" << class_name_uc << "_0";
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string member_name_uc
+ = to_upper_case(to_lower_case(initial_caps_to_underscores((*m_iter)->get_name())));
+
+ f_types_impl_ << "," << endl << indent() << "PROP_" << class_name_uc << "_" << member_name_uc;
+ }
+ f_types_impl_ << endl;
+ indent_down();
+ f_types_impl_ << "};" << endl << endl;
+ }
+
+ // generate struct I/O methods
+ string this_get = this->nspace + name + " * this_object = " + this->nspace_uc + name_uc
+ + "(object);";
+ generate_struct_reader(f_types_impl_, tstruct, "this_object->", this_get);
+ generate_struct_writer(f_types_impl_, tstruct, "this_object->", this_get);
+
+ // generate property setter and getter
+ if (members.size() > 0) {
+ // generate property setter
+ function_name = class_name_lc + "_set_property";
+ args_indent = string(function_name.length() + 2, ' ');
+ f_types_impl_ << "static void" << endl << function_name << " (GObject *object," << endl
+ << args_indent << "guint property_id," << endl << args_indent
+ << "const GValue *value," << endl << args_indent << "GParamSpec *pspec)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
+ << endl << indent() << "switch (property_id)" << endl;
+ scope_up(f_types_impl_);
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* member = (*m_iter);
+ string member_name = member->get_name();
+ string member_name_uc
+ = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
+ t_type* member_type = get_true_type(member->get_type());
+
+ string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;
+
+ f_types_impl_ << indent() << "case " << property_identifier + ":" << endl;
+ indent_up();
+
+ if (member_type->is_base_type()) {
+ t_base_type* base_type = ((t_base_type*)member_type);
+ string assign_function_name;
+
+ if (base_type->get_base() == t_base_type::TYPE_STRING) {
+ string release_function_name;
+
+ f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl;
+ indent_up();
+
+ if (base_type->is_binary()) {
+ release_function_name = "g_byte_array_unref";
+ assign_function_name = "g_value_dup_boxed";
+ } else {
+ release_function_name = "g_free";
+ assign_function_name = "g_value_dup_string";
+ }
+
+ f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");"
+ << endl;
+ indent_down();
+ } else {
+ switch (base_type->get_base()) {
+ case t_base_type::TYPE_BOOL:
+ assign_function_name = "g_value_get_boolean";
+ break;
+
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ assign_function_name = "g_value_get_int";
+ break;
+
+ case t_base_type::TYPE_I64:
+ assign_function_name = "g_value_get_int64";
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ assign_function_name = "g_value_get_double";
+ break;
+
+ default:
+ throw "compiler error: "
+ "unrecognized base type \"" + base_type->get_name() + "\" "
+ "for struct member \""
+ + member_name + "\"";
+ break;
+ }
+ }
+
+ f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name
+ << " (value);" << endl;
+ } else if (member_type->is_enum()) {
+ f_types_impl_ << indent() << "self->" << member_name << " = g_value_get_int (value);"
+ << endl;
+ } else if (member_type->is_container()) {
+ string release_function_name;
+ string assign_function_name;
+
+ if (member_type->is_list()) {
+ t_type* elem_type = ((t_list*)member_type)->get_elem_type();
+
+ // Lists of base types other than strings are represented as GArrays;
+ // all others as GPtrArrays
+ if (is_numeric(elem_type)) {
+ release_function_name = "g_array_unref";
+ } else {
+ release_function_name = "g_ptr_array_unref";
+ }
+
+ assign_function_name = "g_value_dup_boxed";
+ } else if (member_type->is_set() || member_type->is_map()) {
+ release_function_name = "g_hash_table_unref";
+ assign_function_name = "g_value_dup_boxed";
+ }
+
+ f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl;
+ indent_up();
+ f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");"
+ << endl;
+ indent_down();
+ f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name
+ << " (value);" << endl;
+ } else if (member_type->is_struct() || member_type->is_xception()) {
+ f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl;
+ indent_up();
+ f_types_impl_ << indent() << "g_object_unref (self->" << member_name << ");" << endl;
+ indent_down();
+ f_types_impl_ << indent() << "self->" << member_name << " = g_value_dup_object (value);"
+ << endl;
+ }
+
+ if (member->get_req() != t_field::T_REQUIRED) {
+ f_types_impl_ << indent() << "self->__isset_" << member_name << " = TRUE;" << endl;
+ }
+
+ f_types_impl_ << indent() << "break;" << endl << endl;
+ indent_down();
+ }
+ f_types_impl_ << indent() << "default:" << endl;
+ indent_up();
+ f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
+ << endl << indent() << "break;" << endl;
+ indent_down();
+ scope_down(f_types_impl_);
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+
+ // generate property getter
+ function_name = class_name_lc + "_get_property";
+ args_indent = string(function_name.length() + 2, ' ');
+ f_types_impl_ << "static void" << endl << function_name << " (GObject *object," << endl
+ << args_indent << "guint property_id," << endl << args_indent << "GValue *value,"
+ << endl << args_indent << "GParamSpec *pspec)" << endl;
+ scope_up(f_types_impl_);
+ f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl
+ << endl << indent() << "switch (property_id)" << endl;
+ scope_up(f_types_impl_);
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* member = (*m_iter);
+ string member_name = (*m_iter)->get_name();
+ string member_name_uc
+ = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
+ t_type* member_type = get_true_type(member->get_type());
+
+ string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;
+
+ string setter_function_name;
+
+ if (member_type->is_base_type()) {
+ t_base_type* base_type = ((t_base_type*)member_type);
+
+ switch (base_type->get_base()) {
+ case t_base_type::TYPE_BOOL:
+ setter_function_name = "g_value_set_boolean";
+ break;
+
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ setter_function_name = "g_value_set_int";
+ break;
+
+ case t_base_type::TYPE_I64:
+ setter_function_name = "g_value_set_int64";
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ setter_function_name = "g_value_set_double";
+ break;
+
+ case t_base_type::TYPE_STRING:
+ if (base_type->is_binary()) {
+ setter_function_name = "g_value_set_boxed";
+ } else {
+ setter_function_name = "g_value_set_string";
+ }
+ break;
+
+ default:
+ throw "compiler error: "
+ "unrecognized base type \"" + base_type->get_name() + "\" "
+ "for struct member \""
+ + member_name + "\"";
+ break;
+ }
+ } else if (member_type->is_enum()) {
+ setter_function_name = "g_value_set_int";
+ } else if (member_type->is_struct() || member_type->is_xception()) {
+ setter_function_name = "g_value_set_object";
+ } else if (member_type->is_container()) {
+ setter_function_name = "g_value_set_boxed";
+ } else {
+ throw "compiler error: "
+ "unrecognized type for struct member \"" + member_name + "\"";
+ }
+
+ f_types_impl_ << indent() << "case " << property_identifier + ":" << endl;
+ indent_up();
+ f_types_impl_ << indent() << setter_function_name << " (value, self->" << member_name << ");"
+ << endl << indent() << "break;" << endl << endl;
+ indent_down();
+ }
+ f_types_impl_ << indent() << "default:" << endl;
+ indent_up();
+ f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
+ << endl << indent() << "break;" << endl;
+ indent_down();
+ scope_down(f_types_impl_);
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+ }
+
+ // generate the instance init function
+
+ f_types_impl_ << "static void " << endl << this->nspace_lc << name_u << "_instance_init ("
+ << this->nspace << name << " * object)" << endl << "{" << endl;
+ indent_up();
+
+ // generate default-value structures for container-type members
+ bool constant_declaration_output = false;
+ bool string_list_constant_output = false;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* member = *m_iter;
+ t_const_value* member_value = member->get_value();
+
+ if (member_value != NULL) {
+ string member_name = member->get_name();
+ t_type* member_type = get_true_type(member->get_type());
+
+ if (member_type->is_list()) {
+ const vector<t_const_value*>& list = member_value->get_list();
+ t_type* elem_type = ((t_list*)member_type)->get_elem_type();
+
+ // Generate an array with the list literal
+ indent(f_types_impl_) << "static " << type_name(elem_type, false, true) << " __default_"
+ << member_name << "[" << list.size() << "] = " << endl;
+ indent_up();
+ f_types_impl_ << indent() << constant_literal(member_type, member_value) << ";" << endl;
+ indent_down();
+
+ constant_declaration_output = true;
+
+ // If we are generating values for a pointer array (i.e. a list of
+ // strings), set a flag so we know to also declare an index variable to
+ // use in pre-populating the array
+ if (elem_type->is_string()) {
+ string_list_constant_output = true;
+ }
+ }
+
+ // TODO: Handle container types other than list
+ }
+ }
+ if (constant_declaration_output) {
+ if (string_list_constant_output) {
+ indent(f_types_impl_) << "unsigned int list_index;" << endl;
+ }
+
+ f_types_impl_ << endl;
+ }
+
+ // satisfy compilers with -Wall turned on
+ indent(f_types_impl_) << "/* satisfy -Wall */" << endl << indent()
+ << "THRIFT_UNUSED_VAR (object);" << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* member_type = (*m_iter)->get_type();
+ t_type* t = get_true_type(member_type);
+ if (t->is_base_type()) {
+ string dval = " = ";
+ if (t->is_enum()) {
+ dval += "(" + type_name(t) + ")";
+ }
+ t_const_value* cv = (*m_iter)->get_value();
+ if (cv != NULL) {
+ dval += constant_value("", t, cv);
+ } else {
+ dval += t->is_string() ? "NULL" : "0";
+ }
+ indent(f_types_impl_) << "object->" << (*m_iter)->get_name() << dval << ";" << endl;
+ } else if (t->is_struct()) {
+ string name = (*m_iter)->get_name();
+ t_program* type_program = member_type->get_program();
+ string type_nspace = type_program ? type_program->get_namespace("c_glib") : "";
+ string type_nspace_prefix =
+ type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_";
+ string type_name_uc = to_upper_case(initial_caps_to_underscores(member_type->get_name()));
+ indent(f_types_impl_) << "object->" << name << " = g_object_new ("
+ << to_upper_case(type_nspace_prefix) << "TYPE_" << type_name_uc
+ << ", NULL);" << endl;
+ } else if (t->is_xception()) {
+ string name = (*m_iter)->get_name();
+ indent(f_types_impl_) << "object->" << name << " = NULL;" << endl;
+ } else if (t->is_container()) {
+ string name = (*m_iter)->get_name();
+ string init_function;
+ t_type* etype = NULL;
+
+ if (t->is_map()) {
+ t_type* key = ((t_map*)t)->get_key_type();
+ t_type* value = ((t_map*)t)->get_val_type();
+ init_function = generate_new_hash_from_type(key, value);
+ } else if (t->is_set()) {
+ etype = ((t_set*)t)->get_elem_type();
+ init_function = generate_new_hash_from_type(etype, NULL);
+ } else if (t->is_list()) {
+ etype = ((t_list*)t)->get_elem_type();
+ init_function = generate_new_array_from_type(etype);
+ }
+
+ indent(f_types_impl_) << "object->" << name << " = " << init_function << endl;
+
+ // Pre-populate the container with the specified default values, if any
+ if ((*m_iter)->get_value()) {
+ t_const_value* member_value = (*m_iter)->get_value();
+
+ if (t->is_list()) {
+ const vector<t_const_value*>& list = member_value->get_list();
+
+ if (is_numeric(etype)) {
+ indent(f_types_impl_) <<
+ "g_array_append_vals (object->" << name << ", &__default_" <<
+ name << ", " << list.size() << ");" << endl;
+ }
+ else {
+ indent(f_types_impl_) <<
+ "for (list_index = 0; list_index < " << list.size() << "; " <<
+ "list_index += 1)" << endl;
+ indent_up();
+ indent(f_types_impl_) <<
+ "g_ptr_array_add (object->" << name << "," << endl <<
+ indent() << string(17, ' ') << "g_strdup (__default_" <<
+ name << "[list_index]));" << endl;
+ indent_down();
+ }
+ }
+
+ // TODO: Handle container types other than list
+ }
+ }
+
+ /* if not required, initialize the __isset variable */
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ indent(f_types_impl_) << "object->__isset_" << (*m_iter)->get_name() << " = FALSE;" << endl;
+ }
+ }
+
+ indent_down();
+ f_types_impl_ << "}" << endl << endl;
+
+ /* create the destructor */
+ f_types_impl_ << "static void " << endl << this->nspace_lc << name_u
+ << "_finalize (GObject *object)" << endl << "{" << endl;
+ indent_up();
+
+ f_types_impl_ << indent() << this->nspace << name << " *tobject = " << this->nspace_uc << name_uc
+ << " (object);" << endl << endl;
+
+ f_types_impl_ << indent() << "/* satisfy -Wall in case we don't use tobject */" << endl
+ << indent() << "THRIFT_UNUSED_VAR (tobject);" << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if (t->is_container()) {
+ string name = (*m_iter)->get_name();
+ if (t->is_map() || t->is_set()) {
+ f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
+ f_types_impl_ << indent() << "{" << endl;
+ indent_up();
+ f_types_impl_ << indent() << "g_hash_table_destroy (tobject->" << name << ");" << endl;
+ f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
+ indent_down();
+ f_types_impl_ << indent() << "}" << endl;
+ } else if (t->is_list()) {
+ t_type* etype = ((t_list*)t)->get_elem_type();
+ string destructor_function = "g_ptr_array_unref";
+
+ if (etype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot determine array type";
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ destructor_function = "g_array_unref";
+ break;
+ case t_base_type::TYPE_STRING:
+ break;
+ default:
+ throw "compiler error: no array info for type";
+ }
+ } else if (etype->is_enum()) {
+ destructor_function = "g_array_unref";
+ }
+
+ f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
+ f_types_impl_ << indent() << "{" << endl;
+ indent_up();
+ f_types_impl_ << indent() << destructor_function << " (tobject->" << name << ");" << endl;
+ f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
+ indent_down();
+ f_types_impl_ << indent() << "}" << endl;
+ }
+ } else if (t->is_struct() || t->is_xception()) {
+ string name = (*m_iter)->get_name();
+ // TODO: g_clear_object needs glib >= 2.28
+ // f_types_impl_ << indent() << "g_clear_object (&(tobject->" << name << "));" << endl;
+ // does g_object_unref the trick?
+ f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
+ f_types_impl_ << indent() << "{" << endl;
+ indent_up();
+ f_types_impl_ << indent() << "g_object_unref(tobject->" << name << ");" << endl;
+ f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
+ indent_down();
+ f_types_impl_ << indent() << "}" << endl;
+ } else if (t->is_string()) {
+ string name = (*m_iter)->get_name();
+ f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl;
+ f_types_impl_ << indent() << "{" << endl;
+ indent_up();
+ f_types_impl_ << indent() << generate_free_func_from_type(t) << "(tobject->" << name << ");"
+ << endl;
+ f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl;
+ indent_down();
+ f_types_impl_ << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ f_types_impl_ << "}" << endl << endl;
+
+ // generate the class init function
+
+ f_types_impl_ << "static void" << endl << class_name_lc << "_class_init (" << class_name
+ << "Class * cls)" << endl;
+ scope_up(f_types_impl_);
+
+ f_types_impl_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl
+ << indent() << "ThriftStructClass *struct_class = "
+ << "THRIFT_STRUCT_CLASS (cls);" << endl << endl << indent()
+ << "struct_class->read = " << class_name_lc << "_read;" << endl << indent()
+ << "struct_class->write = " << class_name_lc << "_write;" << endl << endl
+ << indent() << "gobject_class->finalize = " << class_name_lc << "_finalize;"
+ << endl;
+ if (members.size() > 0) {
+ f_types_impl_ << indent() << "gobject_class->get_property = " << class_name_lc
+ << "_get_property;" << endl << indent()
+ << "gobject_class->set_property = " << class_name_lc << "_set_property;" << endl;
+
+ // install a property for each member
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* member = (*m_iter);
+ string member_name = member->get_name();
+ string member_name_uc
+ = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
+ t_type* member_type = get_true_type(member->get_type());
+ t_const_value* member_value = member->get_value();
+
+ string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;
+
+ f_types_impl_ << endl << indent() << "g_object_class_install_property" << endl;
+ indent_up();
+ args_indent = indent() + ' ';
+ f_types_impl_ << indent() << "(gobject_class," << endl << args_indent << property_identifier
+ << "," << endl << args_indent;
+
+ if (member_type->is_base_type()) {
+ t_base_type::t_base base_type = ((t_base_type*)member_type)->get_base();
+
+ if (base_type == t_base_type::TYPE_STRING) {
+ if (((t_base_type*)member_type)->is_binary()) {
+ args_indent += string(20, ' ');
+ f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent
+ << "NULL," << endl << args_indent << "NULL," << endl << args_indent
+ << "G_TYPE_BYTE_ARRAY," << endl << args_indent << "G_PARAM_READWRITE));"
+ << endl;
+ } else {
+ args_indent += string(21, ' ');
+ f_types_impl_ << "g_param_spec_string (\"" << member_name << "\"," << endl
+ << args_indent << "NULL," << endl << args_indent << "NULL," << endl
+ << args_indent
+ << ((member_value != NULL) ? "\"" + member_value->get_string() + "\""
+ : "NULL") << "," << endl << args_indent
+ << "G_PARAM_READWRITE));" << endl;
+ }
+ } else if (base_type == t_base_type::TYPE_BOOL) {
+ args_indent += string(22, ' ');
+ f_types_impl_ << "g_param_spec_boolean (\"" << member_name << "\"," << endl << args_indent
+ << "NULL," << endl << args_indent << "NULL," << endl << args_indent
+ << (((member_value != NULL) && (member_value->get_integer() != 0))
+ ? "TRUE"
+ : "FALSE") << "," << endl << args_indent << "G_PARAM_READWRITE));"
+ << endl;
+ } else if ((base_type == t_base_type::TYPE_I8) || (base_type == t_base_type::TYPE_I16)
+ || (base_type == t_base_type::TYPE_I32) || (base_type == t_base_type::TYPE_I64)
+ || (base_type == t_base_type::TYPE_DOUBLE)) {
+ string param_spec_function_name = "g_param_spec_int";
+ string min_value;
+ string max_value;
+ ostringstream default_value;
+
+ switch (base_type) {
+ case t_base_type::TYPE_I8:
+ min_value = "G_MININT8";
+ max_value = "G_MAXINT8";
+ break;
+
+ case t_base_type::TYPE_I16:
+ min_value = "G_MININT16";
+ max_value = "G_MAXINT16";
+ break;
+
+ case t_base_type::TYPE_I32:
+ min_value = "G_MININT32";
+ max_value = "G_MAXINT32";
+ break;
+
+ case t_base_type::TYPE_I64:
+ param_spec_function_name = "g_param_spec_int64";
+ min_value = "G_MININT64";
+ max_value = "G_MAXINT64";
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ param_spec_function_name = "g_param_spec_double";
+ min_value = "-INFINITY";
+ max_value = "INFINITY";
+ break;
+
+ default:
+ throw "compiler error: "
+ "unrecognized base type \"" + member_type->get_name() + "\" "
+ "for struct member \""
+ + member_name + "\"";
+ break;
+ }
+
+ if (member_value != NULL) {
+ default_value << (base_type == t_base_type::TYPE_DOUBLE ? member_value->get_double()
+ : member_value->get_integer());
+ } else {
+ default_value << "0";
+ }
+
+ args_indent += string(param_spec_function_name.length() + 2, ' ');
+ f_types_impl_ << param_spec_function_name << " (\"" << member_name << "\"," << endl
+ << args_indent << "NULL," << endl << args_indent << "NULL," << endl
+ << args_indent << min_value << "," << endl << args_indent << max_value
+ << "," << endl << args_indent << default_value.str() << "," << endl
+ << args_indent << "G_PARAM_READWRITE));" << endl;
+ }
+
+ indent_down();
+ } else if (member_type->is_enum()) {
+ t_enum_value* enum_min_value = ((t_enum*)member_type)->get_min_value();
+ t_enum_value* enum_max_value = ((t_enum*)member_type)->get_max_value();
+ int min_value = (enum_min_value != NULL) ? enum_min_value->get_value() : 0;
+ int max_value = (enum_max_value != NULL) ? enum_max_value->get_value() : 0;
+
+ args_indent += string(18, ' ');
+ f_types_impl_ << "g_param_spec_int (\"" << member_name << "\"," << endl << args_indent
+ << "NULL," << endl << args_indent << "NULL," << endl << args_indent
+ << min_value << "," << endl << args_indent << max_value << "," << endl
+ << args_indent << min_value << "," << endl << args_indent
+ << "G_PARAM_READWRITE));" << endl;
+ indent_down();
+ } else if (member_type->is_struct() || member_type->is_xception()) {
+ t_program* type_program = member_type->get_program();
+ string type_nspace = type_program ? type_program->get_namespace("c_glib") : "";
+ string type_nspace_prefix =
+ type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_";
+
+ string param_type = to_upper_case(type_nspace_prefix) + "TYPE_"
+ + to_upper_case(initial_caps_to_underscores(member_type->get_name()));
+
+ args_indent += string(20, ' ');
+ f_types_impl_ << "g_param_spec_object (\"" << member_name << "\"," << endl << args_indent
+ << "NULL," << endl << args_indent << "NULL," << endl << args_indent
+ << param_type << "," << endl << args_indent << "G_PARAM_READWRITE));" << endl;
+ indent_down();
+ } else if (member_type->is_list()) {
+ t_type* elem_type = ((t_list*)member_type)->get_elem_type();
+ string param_type;
+
+ if (elem_type->is_base_type() && !elem_type->is_string()) {
+ param_type = "G_TYPE_ARRAY";
+ } else {
+ param_type = "G_TYPE_PTR_ARRAY";
+ }
+
+ args_indent += string(20, ' ');
+ f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent
+ << "NULL," << endl << args_indent << "NULL," << endl << args_indent
+ << param_type << "," << endl << args_indent << "G_PARAM_READWRITE));" << endl;
+ indent_down();
+ } else if (member_type->is_set() || member_type->is_map()) {
+ args_indent += string(20, ' ');
+ f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent
+ << "NULL," << endl << args_indent << "NULL," << endl << args_indent
+ << "G_TYPE_HASH_TABLE," << endl << args_indent << "G_PARAM_READWRITE));"
+ << endl;
+ indent_down();
+ }
+ }
+ }
+ scope_down(f_types_impl_);
+ f_types_impl_ << endl;
+
+ f_types_impl_ << "GType" << endl << this->nspace_lc << name_u << "_get_type (void)" << endl << "{"
+ << endl << " static GType type = 0;" << endl << endl << " if (type == 0) " << endl
+ << " {" << endl << " static const GTypeInfo type_info = " << endl << " {"
+ << endl << " sizeof (" << this->nspace << name << "Class)," << endl
+ << " NULL, /* base_init */" << endl << " NULL, /* base_finalize */"
+ << endl << " (GClassInitFunc) " << this->nspace_lc << name_u << "_class_init,"
+ << endl << " NULL, /* class_finalize */" << endl
+ << " NULL, /* class_data */" << endl << " sizeof (" << this->nspace
+ << name << ")," << endl << " 0, /* n_preallocs */" << endl
+ << " (GInstanceInitFunc) " << this->nspace_lc << name_u << "_instance_init,"
+ << endl << " NULL, /* value_table */" << endl << " };" << endl << endl
+ << " type = g_type_register_static (THRIFT_TYPE_STRUCT, " << endl
+ << " \"" << this->nspace << name << "Type\","
+ << endl << " &type_info, 0);" << endl << " }"
+ << endl << endl << " return type;" << endl << "}" << endl << endl;
+}
+
+/**
+ * Generates functions to write Thrift structures to a stream.
+ */
+void t_c_glib_generator::generate_struct_writer(ostream& out,
+ t_struct* tstruct,
+ string this_name,
+ string this_get,
+ bool is_function) {
+ string name = tstruct->get_name();
+ string name_u = initial_caps_to_underscores(name);
+ string name_uc = to_upper_case(name_u);
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ int error_ret = 0;
+
+ if (is_function) {
+ error_ret = -1;
+ indent(out) << "static gint32" << endl << this->nspace_lc << name_u
+ << "_write (ThriftStruct *object, ThriftProtocol *protocol, GError **error)"
+ << endl;
+ }
+ indent(out) << "{" << endl;
+ indent_up();
+
+ out << indent() << "gint32 ret;" << endl << indent() << "gint32 xfer = 0;" << endl << endl;
+
+ indent(out) << this_get << endl;
+ // satisfy -Wall in the case of an empty struct
+ if (!this_get.empty()) {
+ indent(out) << "THRIFT_UNUSED_VAR (this_object);" << endl;
+ }
+
+ out << indent() << "if ((ret = thrift_protocol_write_struct_begin (protocol, \"" << name
+ << "\", error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl
+ << indent() << "xfer += ret;" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL) {
+ indent(out) << "if (this_object->__isset_" << (*f_iter)->get_name() << " == TRUE) {" << endl;
+ indent_up();
+ }
+
+ out << indent() << "if ((ret = thrift_protocol_write_field_begin (protocol, "
+ << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", "
+ << (*f_iter)->get_key() << ", error)) < 0)" << endl << indent() << " return " << error_ret
+ << ";" << endl << indent() << "xfer += ret;" << endl;
+ generate_serialize_field(out, *f_iter, this_name, "", error_ret);
+ out << indent() << "if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;"
+ << endl;
+
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ // write the struct map
+ out << indent() << "if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
+ << indent() << "if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
+ << endl;
+
+ if (is_function) {
+ indent(out) << "return xfer;" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates code to read Thrift structures from a stream.
+ */
+void t_c_glib_generator::generate_struct_reader(ostream& out,
+ t_struct* tstruct,
+ string this_name,
+ string this_get,
+ bool is_function) {
+ string name = tstruct->get_name();
+ string name_u = initial_caps_to_underscores(name);
+ string name_uc = to_upper_case(name_u);
+ int error_ret = 0;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (is_function) {
+ error_ret = -1;
+ indent(out) << "/* reads a " << name_u << " object */" << endl << "static gint32" << endl
+ << this->nspace_lc << name_u
+ << "_read (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << endl;
+ }
+
+ indent(out) << "{" << endl;
+ indent_up();
+
+ // declare stack temp variables
+ out << indent() << "gint32 ret;" << endl << indent() << "gint32 xfer = 0;" << endl << indent()
+ << "gchar *name = NULL;" << endl << indent() << "ThriftType ftype;" << endl << indent()
+ << "gint16 fid;" << endl << indent() << "guint32 len = 0;" << endl << indent()
+ << "gpointer data = NULL;" << endl << indent() << this_get << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ indent(out) << "gboolean isset_" << (*f_iter)->get_name() << " = FALSE;" << endl;
+ }
+ }
+
+ out << endl;
+
+ // satisfy -Wall in case we don't use some variables
+ out << indent() << "/* satisfy -Wall in case these aren't used */" << endl << indent()
+ << "THRIFT_UNUSED_VAR (len);" << endl << indent() << "THRIFT_UNUSED_VAR (data);" << endl;
+
+ if (!this_get.empty()) {
+ out << indent() << "THRIFT_UNUSED_VAR (this_object);" << endl;
+ }
+ out << endl;
+
+ // read the beginning of the structure marker
+ out << indent() << "/* read the struct begin marker */" << endl << indent()
+ << "if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0)" << endl
+ << indent() << "{" << endl << indent() << " if (name) g_free (name);" << endl << indent()
+ << " return " << error_ret << ";" << endl << indent() << "}" << endl << indent()
+ << "xfer += ret;" << endl << indent() << "if (name) g_free (name);" << endl << indent()
+ << "name = NULL;" << endl << endl;
+
+ // read the struct fields
+ out << indent() << "/* read the struct fields */" << endl << indent() << "while (1)" << endl;
+ scope_up(out);
+
+ // read beginning field marker
+ out << indent() << "/* read the beginning of a field */" << endl << indent()
+ << "if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype, &fid, error)) < 0)"
+ << endl << indent() << "{" << endl << indent() << " if (name) g_free (name);" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "}" << endl << indent()
+ << "xfer += ret;" << endl << indent() << "if (name) g_free (name);" << endl << indent()
+ << "name = NULL;" << endl << endl;
+
+ // check for field STOP marker
+ out << indent() << "/* break if we get a STOP field */" << endl << indent()
+ << "if (ftype == T_STOP)" << endl << indent() << "{" << endl << indent() << " break;" << endl
+ << indent() << "}" << endl << endl;
+
+ // switch depending on the field type
+ indent(out) << "switch (fid)" << endl;
+
+ // start switch
+ scope_up(out);
+
+ // generate deserialization code for known types
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ")" << endl;
+ indent(out) << "{" << endl;
+
+ indent_up();
+ // generate deserialize field
+ generate_deserialize_field(out, *f_iter, this_name, "", error_ret, false);
+ indent_down();
+
+ out << indent() << "} else {" << endl << indent()
+ << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << indent()
+ << " return " << error_ret << ";" << endl << indent() << " xfer += ret;" << endl
+ << indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ // create the default case
+ out << indent() << "default:" << endl << indent()
+ << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << indent()
+ << " return " << error_ret << ";" << endl << indent() << " xfer += ret;" << endl
+ << indent() << " break;" << endl;
+
+ // end switch
+ scope_down(out);
+
+ // read field end marker
+ out << indent() << "if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl;
+
+ // end while loop
+ scope_down(out);
+ out << endl;
+
+ // read the end of the structure
+ out << indent() << "if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
+ << endl;
+
+ // if a required field is missing, throw an error
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ out << indent() << "if (!isset_" << (*f_iter)->get_name() << ")" << endl << indent() << "{"
+ << endl << indent() << " g_set_error (error, THRIFT_PROTOCOL_ERROR," << endl << indent()
+ << " THRIFT_PROTOCOL_ERROR_INVALID_DATA," << endl << indent()
+ << " \"missing field\");" << endl << indent() << " return -1;" << endl
+ << indent() << "}" << endl << endl;
+ }
+ }
+
+ if (is_function) {
+ indent(out) << "return xfer;" << endl;
+ }
+
+ // end the function/structure
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_c_glib_generator::generate_serialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ string suffix,
+ int error_ret) {
+ t_type* type = get_true_type(tfield->get_type());
+ string name = prefix + tfield->get_name() + suffix;
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name, error_ret);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, name, error_ret);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << "if ((ret = thrift_protocol_write_";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "bool (protocol, " << name;
+ break;
+ case t_base_type::TYPE_I8:
+ out << "byte (protocol, " << name;
+ break;
+ case t_base_type::TYPE_I16:
+ out << "i16 (protocol, " << name;
+ break;
+ case t_base_type::TYPE_I32:
+ out << "i32 (protocol, " << name;
+ break;
+ case t_base_type::TYPE_I64:
+ out << "i64 (protocol, " << name;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "double (protocol, " << name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "binary (protocol, " << name << " ? ((GByteArray *) " << name << ")->data : NULL, "
+ << name << " ? ((GByteArray *) " << name << ")->len : 0";
+ } else {
+ out << "string (protocol, " << name;
+ }
+ break;
+ default:
+ throw "compiler error: no C writer for base type " + t_base_type::t_base_name(tbase) + name;
+ }
+ } else {
+ out << "i32 (protocol, (gint32) " << name;
+ }
+ out << ", error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl
+ << indent() << "xfer += ret;" << endl << endl;
+ } else {
+ throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + name + "' TYPE '"
+ + type_name(type));
+ }
+}
+
+void t_c_glib_generator::generate_serialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix,
+ int error_ret) {
+ (void)tstruct;
+ out << indent() << "if ((ret = thrift_struct_write (THRIFT_STRUCT (" << prefix
+ << "), protocol, error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl
+ << indent() << "xfer += ret;" << endl << endl;
+}
+
+void t_c_glib_generator::generate_serialize_container(ostream& out,
+ t_type* ttype,
+ string prefix,
+ int error_ret) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ t_type* tkey = ((t_map*)ttype)->get_key_type();
+ t_type* tval = ((t_map*)ttype)->get_val_type();
+ string tkey_name = type_name(tkey);
+ string tval_name = type_name(tval);
+ string tkey_ptr;
+ string tval_ptr;
+ string keyname = tmp("key");
+ string valname = tmp("val");
+
+ declore_local_variable_for_write(out, tkey, keyname);
+ declore_local_variable_for_write(out, tval, valname);
+
+ /* If either the key or value type is a typedef, find its underlying type so
+ we can correctly determine how to generate a pointer to it */
+ tkey = get_true_type(tkey);
+ tval = get_true_type(tval);
+
+ tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*";
+ tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*";
+
+ /*
+ * Some ugliness here. To maximize backwards compatibility, we
+ * avoid using GHashTableIter and instead get a GList of all keys,
+ * then copy it into a array on the stack, and free it.
+ * This is because we may exit early before we get a chance to free the
+ * GList.
+ */
+ out << indent() << "GList *key_list = NULL, *iter = NULL;" << endl
+ << indent() << tkey_name << tkey_ptr << "* keys;" << endl
+ << indent() << "int i = 0, key_count;" << endl
+ << endl
+ << indent() << "if ((ret = thrift_protocol_write_map_begin (protocol, "
+ << type_to_enum(tkey) << ", " << type_to_enum(tval) << ", " << prefix << " ? "
+ << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0"
+ << ", error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << indent() << "if (" << prefix << ")" << endl
+ << indent() << " g_hash_table_foreach ((GHashTable *) " << prefix
+ << ", thrift_hash_table_get_keys, &key_list);" << endl
+ << indent() << "key_count = g_list_length (key_list);" << endl
+ << indent() << "keys = g_newa (" << tkey_name << tkey_ptr
+ << ", key_count);" << endl
+ << indent() << "for (iter = g_list_first (key_list); iter; "
+ "iter = iter->next)" << endl;
+ indent_up();
+ out << indent() << "keys[i++] = (" << tkey_name << tkey_ptr
+ << ") iter->data;" << endl;
+ indent_down();
+ out << indent() << "g_list_free (key_list);" << endl
+ << endl
+ << indent() << "for (i = 0; i < key_count; ++i)" << endl;
+ scope_up(out);
+ out << indent() << keyname << " = keys[i];" << endl
+ << indent() << valname << " = (" << tval_name << tval_ptr
+ << ") g_hash_table_lookup (((GHashTable *) " << prefix
+ << "), (gpointer) " << keyname << ");" << endl
+ << endl;
+ generate_serialize_map_element(out,
+ (t_map*)ttype,
+ tkey_ptr + " " + keyname,
+ tval_ptr + " " + valname,
+ error_ret);
+ scope_down(out);
+ out << indent() << "if ((ret = thrift_protocol_write_map_end (protocol, "
+ "error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl;
+ } else if (ttype->is_set()) {
+ t_type* telem = ((t_set*)ttype)->get_elem_type();
+ string telem_name = type_name(telem);
+ string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*";
+ out << indent() << "GList *key_list = NULL, *iter = NULL;" << endl
+ << indent() << telem_name << telem_ptr << "* keys;" << endl
+ << indent() << "int i = 0, key_count;" << endl
+ << indent() << telem_name << telem_ptr << " elem;" << endl
+ << indent() << "gpointer value;" << endl
+ << indent() << "THRIFT_UNUSED_VAR (value);" << endl
+ << endl
+ << indent() << "if ((ret = thrift_protocol_write_set_begin (protocol, "
+ << type_to_enum(telem) << ", " << prefix << " ? "
+ << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0"
+ << ", error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << indent() << "if (" << prefix << ")" << endl
+ << indent() << " g_hash_table_foreach ((GHashTable *) " << prefix
+ << ", thrift_hash_table_get_keys, &key_list);" << endl
+ << indent() << "key_count = g_list_length (key_list);" << endl
+ << indent() << "keys = g_newa (" << telem_name << telem_ptr
+ << ", key_count);" << endl
+ << indent() << "for (iter = g_list_first (key_list); iter; "
+ "iter = iter->next)" << endl;
+ indent_up();
+ out << indent() << "keys[i++] = (" << telem_name << telem_ptr
+ << ") iter->data;" << endl;
+ indent_down();
+ out << indent() << "g_list_free (key_list);" << endl
+ << endl
+ << indent() << "for (i = 0; i < key_count; ++i)" << endl;
+ scope_up(out);
+ out << indent() << "elem = keys[i];" << endl
+ << indent() << "value = (gpointer) g_hash_table_lookup "
+ "(((GHashTable *) " << prefix << "), (gpointer) elem);" << endl
+ << endl;
+ generate_serialize_set_element(out,
+ (t_set*)ttype,
+ telem_ptr + "elem",
+ error_ret);
+ scope_down(out);
+ out << indent() << "if ((ret = thrift_protocol_write_set_end (protocol, "
+ "error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl;
+ } else if (ttype->is_list()) {
+ string length = "(" + prefix + " ? " + prefix + "->len : 0)";
+ string i = tmp("i");
+ out << indent() << "guint " << i << ";" << endl
+ << endl
+ << indent() << "if ((ret = thrift_protocol_write_list_begin (protocol, "
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", (gint32) "
+ << length << ", error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << indent() << "for (" << i << " = 0; " << i << " < " << length << "; "
+ << i << "++)" << endl;
+ scope_up(out);
+ generate_serialize_list_element(out, (t_list*)ttype, prefix, i, error_ret);
+ scope_down(out);
+ out << indent() << "if ((ret = thrift_protocol_write_list_end (protocol, "
+ "error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl;
+ }
+
+ scope_down(out);
+}
+
+void t_c_glib_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string key,
+ string value,
+ int error_ret) {
+ t_field kfield(tmap->get_key_type(), key);
+ generate_serialize_field(out, &kfield, "", "", error_ret);
+
+ t_field vfield(tmap->get_val_type(), value);
+ generate_serialize_field(out, &vfield, "", "", error_ret);
+}
+
+void t_c_glib_generator::generate_serialize_set_element(ostream& out,
+ t_set* tset,
+ string element,
+ int error_ret) {
+ t_field efield(tset->get_elem_type(), element);
+ generate_serialize_field(out, &efield, "", "", error_ret);
+}
+
+void t_c_glib_generator::generate_serialize_list_element(ostream& out,
+ t_list* tlist,
+ string list,
+ string index,
+ int error_ret) {
+ t_type* ttype = get_true_type(tlist->get_elem_type());
+
+ // cast to non-const
+ string cast = "";
+ string name = "g_ptr_array_index ((GPtrArray *) " + list + ", " + index + ")";
+
+ if (ttype->is_void()) {
+ throw std::runtime_error("compiler error: list element type cannot be void");
+ } else if (is_numeric(ttype)) {
+ name = "g_array_index (" + list + ", " + base_type_name(ttype) + ", " + index + ")";
+ } else if (ttype->is_string()) {
+ cast = "(gchar*)";
+ } else if (ttype->is_map() || ttype->is_set()) {
+ cast = "(GHashTable*)";
+ } else if (ttype->is_list()) {
+ t_type* etype = ((t_list*)ttype)->get_elem_type();
+ if (etype->is_void()) {
+ throw std::runtime_error("compiler error: list element type cannot be void");
+ }
+ cast = is_numeric(etype) ? "(GArray*)" : "(GPtrArray*)";
+ }
+
+ t_field efield(ttype, "(" + cast + name + ")");
+ generate_serialize_field(out, &efield, "", "", error_ret);
+}
+
+/* deserializes a field of any type. */
+void t_c_glib_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ string suffix,
+ int error_ret,
+ bool allocate) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix
+ + tfield->get_name());
+ }
+
+ string name = prefix + tfield->get_name() + suffix;
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name, error_ret, allocate);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name, error_ret);
+ } else if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ if (tbase == t_base_type::TYPE_STRING) {
+ indent(out) << "if (" << name << " != NULL)" << endl << indent() << "{" << endl;
+ indent_up();
+ indent(out) << "g_free(" << name << ");" << endl << indent() << name << " = NULL;" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+ indent(out) << "if ((ret = thrift_protocol_read_";
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "binary (protocol, &data, &len";
+ } else {
+ out << "string (protocol, &" << name;
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "bool (protocol, &" << name;
+ break;
+ case t_base_type::TYPE_I8:
+ out << "byte (protocol, &" << name;
+ break;
+ case t_base_type::TYPE_I16:
+ out << "i16 (protocol, &" << name;
+ break;
+ case t_base_type::TYPE_I32:
+ out << "i32 (protocol, &" << name;
+ break;
+ case t_base_type::TYPE_I64:
+ out << "i64 (protocol, &" << name;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "double (protocol, &" << name;
+ break;
+ default:
+ throw "compiler error: no C reader for base type " + t_base_type::t_base_name(tbase) + name;
+ }
+ out << ", error)) < 0)" << endl;
+ out << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;"
+ << endl;
+
+ // load the byte array with the data
+ if (tbase == t_base_type::TYPE_STRING && type->is_binary()) {
+ indent(out) << name << " = g_byte_array_new();" << endl;
+ indent(out) << "g_byte_array_append (" << name << ", (guint8 *) data, (guint) len);" << endl;
+ indent(out) << "g_free (data);" << endl;
+ }
+ } else if (type->is_enum()) {
+ string t = tmp("ecast");
+ out << indent() << "gint32 " << t << ";" << endl << indent()
+ << "if ((ret = thrift_protocol_read_i32 (protocol, &" << t << ", error)) < 0)" << endl
+ << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl
+ << indent() << name << " = (" << type_name(type) << ")" << t << ";" << endl;
+ } else {
+ throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + tfield->get_name() + "' TYPE '"
+ + type_name(type));
+ }
+
+ // if the type is not required and this is a thrift struct (no prefix),
+ // set the isset variable. if the type is required, then set the
+ // local variable indicating the value was set, so that we can do // validation later.
+ if (prefix != "" && tfield->get_req() != t_field::T_REQUIRED) {
+ indent(out) << prefix << "__isset_" << tfield->get_name() << suffix << " = TRUE;" << endl;
+ } else if (prefix != "" && tfield->get_req() == t_field::T_REQUIRED) {
+ indent(out) << "isset_" << tfield->get_name() << " = TRUE;" << endl;
+ }
+}
+
+void t_c_glib_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix,
+ int error_ret,
+ bool allocate) {
+ string name_uc = to_upper_case(initial_caps_to_underscores(tstruct->get_name()));
+ if (tstruct->is_xception()) {
+ out << indent() << "/* This struct is an exception */" << endl;
+ allocate = true;
+ }
+
+ if (allocate) {
+ out << indent() << "if ( " << prefix << " != NULL)" << endl << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "g_object_unref (" << prefix << ");" << endl;
+ indent_down();
+ out << indent() << "}" << endl << indent() << prefix << " = g_object_new (" << this->nspace_uc
+ << "TYPE_" << name_uc << ", NULL);" << endl;
+ }
+ out << indent() << "if ((ret = thrift_struct_read (THRIFT_STRUCT (" << prefix
+ << "), protocol, error)) < 0)" << endl << indent() << "{" << endl;
+ indent_up();
+ if (allocate) {
+ indent(out) << "g_object_unref (" << prefix << ");" << endl;
+ if (tstruct->is_xception()) {
+ indent(out) << prefix << " = NULL;" << endl;
+ }
+ }
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl << indent() << "xfer += ret;" << endl;
+}
+
+void t_c_glib_generator::generate_deserialize_container(ostream& out,
+ t_type* ttype,
+ string prefix,
+ int error_ret) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ out << indent() << "guint32 size;" << endl
+ << indent() << "guint32 i;" << endl
+ << indent() << "ThriftType key_type;" << endl
+ << indent() << "ThriftType value_type;" << endl
+ << endl
+ << indent() << "/* read the map begin marker */" << endl
+ << indent() << "if ((ret = thrift_protocol_read_map_begin (protocol, "
+ "&key_type, &value_type, &size, error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << endl;
+
+ // iterate over map elements
+ out << indent() << "/* iterate through each of the map's fields */" << endl
+ << indent() << "for (i = 0; i < size; i++)" << endl;
+ scope_up(out);
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix, error_ret);
+ scope_down(out);
+ out << endl;
+
+ // read map end
+ out << indent() << "/* read the map end marker */" << endl
+ << indent() << "if ((ret = thrift_protocol_read_map_end (protocol, "
+ "error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "guint32 size;" << endl
+ << indent() << "guint32 i;" << endl
+ << indent() << "ThriftType element_type;" << endl
+ << endl
+ << indent() << "if ((ret = thrift_protocol_read_set_begin (protocol, "
+ "&element_type, &size, error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << endl;
+
+ // iterate over the elements
+ out << indent() << "/* iterate through the set elements */" << endl
+ << indent() << "for (i = 0; i < size; ++i)" << endl;
+ scope_up(out);
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix, error_ret);
+ scope_down(out);
+
+ // read set end
+ out << indent() << "if ((ret = thrift_protocol_read_set_end (protocol, "
+ "error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "guint32 size;" << endl
+ << indent() << "guint32 i;" << endl
+ << indent() << "ThriftType element_type;" << endl
+ << endl
+ << indent() << "if ((ret = thrift_protocol_read_list_begin (protocol, "
+ "&element_type,&size, error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl
+ << endl;
+
+ // iterate over the elements
+ out << indent() << "/* iterate through list elements */" << endl
+ << indent() << "for (i = 0; i < size; i++)" << endl;
+ scope_up(out);
+ generate_deserialize_list_element(out,
+ (t_list*)ttype,
+ prefix,
+ "i",
+ error_ret);
+ scope_down(out);
+
+ // read list end
+ out << indent() << "if ((ret = thrift_protocol_read_list_end (protocol, "
+ "error)) < 0)" << endl;
+ indent_up();
+ out << indent() << "return " << error_ret << ";" << endl;
+ indent_down();
+ out << indent() << "xfer += ret;" << endl;
+ }
+
+ scope_down(out);
+}
+
+void t_c_glib_generator::declare_local_variable(ostream& out, t_type* ttype, string& name, bool for_hash_table) {
+ string tname = type_name(ttype);
+
+ /* If the given type is a typedef, find its underlying type so we
+ can correctly determine how to generate a pointer to it */
+ ttype = get_true_type(ttype);
+ string ptr = !is_numeric(ttype) ? "" : "*";
+
+ if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ out << indent() << tname << ptr << " " << name << " = "
+ << generate_new_hash_from_type(tmap->get_key_type(), tmap->get_val_type()) << endl;
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ out << indent() << tname << ptr << " " << name << " = "
+ << generate_new_array_from_type(tlist->get_elem_type()) << endl;
+ } else if (for_hash_table && ttype->is_enum()) {
+ out << indent() << tname << " " << name << ";" << endl;
+ } else {
+ out << indent() << tname << ptr << " " << name
+ << (ptr != "" ? " = g_new (" + tname + ", 1)" : " = NULL") << ";" << endl;
+ }
+}
+
+void t_c_glib_generator::declore_local_variable_for_write(ostream& out,
+ t_type* ttype,
+ string& name) {
+ string tname = type_name(ttype);
+ ttype = get_true_type(ttype);
+ string ptr = ttype->is_string() || !ttype->is_base_type() ? " " : "* ";
+ string init_val = ttype->is_enum() ? "" : " = NULL";
+ out << indent() << tname << ptr << name << init_val << ";" << endl;
+}
+
+void t_c_glib_generator::generate_deserialize_map_element(ostream& out,
+ t_map* tmap,
+ string prefix,
+ int error_ret) {
+ t_type* tkey = tmap->get_key_type();
+ t_type* tval = tmap->get_val_type();
+ string keyname = tmp("key");
+ string valname = tmp("val");
+
+ declare_local_variable(out, tkey, keyname, true);
+ declare_local_variable(out, tval, valname, true);
+
+ /* If either the key or value type is a typedef, find its underlying
+ type so we can correctly determine how to generate a pointer to
+ it */
+ tkey = get_true_type(tkey);
+ tval = get_true_type(tval);
+
+ string tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*";
+ string tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*";
+
+ // deserialize the fields of the map element
+ t_field fkey(tkey, tkey_ptr + keyname);
+ generate_deserialize_field(out, &fkey, "", "", error_ret);
+ t_field fval(tval, tval_ptr + valname);
+ generate_deserialize_field(out, &fval, "", "", error_ret);
+
+ indent(out) << "if (" << prefix << " && " << keyname << ")" << endl;
+ indent_up();
+ indent(out) << "g_hash_table_insert ((GHashTable *)" << prefix << ", (gpointer) " << keyname
+ << ", (gpointer) " << valname << ");" << endl;
+ indent_down();
+}
+
+void t_c_glib_generator::generate_deserialize_set_element(ostream& out,
+ t_set* tset,
+ string prefix,
+ int error_ret) {
+ t_type* telem = tset->get_elem_type();
+ string elem = tmp("_elem");
+ string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*";
+
+ declare_local_variable(out, telem, elem, true);
+
+ t_field felem(telem, telem_ptr + elem);
+ generate_deserialize_field(out, &felem, "", "", error_ret);
+
+ indent(out) << "if (" << prefix << " && " << elem << ")" << endl;
+ indent_up();
+ indent(out) << "g_hash_table_insert ((GHashTable *) " << prefix << ", (gpointer) " << elem
+ << ", (gpointer) " << elem << ");" << endl;
+ indent_down();
+}
+
+void t_c_glib_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix,
+ string index,
+ int error_ret) {
+ (void)index;
+ t_type* ttype = get_true_type(tlist->get_elem_type());
+ string elem = tmp("_elem");
+ string telem_ptr = !is_numeric(ttype) ? "" : "*";
+
+ declare_local_variable(out, ttype, elem, false);
+
+ t_field felem(ttype, telem_ptr + elem);
+ generate_deserialize_field(out, &felem, "", "", error_ret);
+
+ if (ttype->is_void()) {
+ throw std::runtime_error("compiler error: list element type cannot be void");
+ } else if (is_numeric(ttype)) {
+ indent(out) << "g_array_append_vals (" << prefix << ", " << elem << ", 1);" << endl;
+ } else {
+ indent(out) << "g_ptr_array_add (" << prefix << ", " << elem << ");" << endl;
+ }
+}
+
+string t_c_glib_generator::generate_free_func_from_type(t_type* ttype) {
+ if (ttype == NULL)
+ return "NULL";
+
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot determine hash type";
+ break;
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ return "g_free";
+ case t_base_type::TYPE_STRING:
+ if (((t_base_type*)ttype)->is_binary()) {
+ return "thrift_string_free";
+ }
+ return "g_free";
+ default:
+ throw "compiler error: no hash table info for type";
+ }
+ } else if (ttype->is_enum()) {
+ return "NULL";
+ } else if (ttype->is_map() || ttype->is_set()) {
+ return "(GDestroyNotify) thrift_safe_hash_table_destroy";
+ } else if (ttype->is_struct()) {
+ return "g_object_unref";
+ } else if (ttype->is_list()) {
+ t_type* etype = ((t_list*)ttype)->get_elem_type();
+ if (etype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot determine array type";
+ break;
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ return "(GDestroyNotify) g_array_unref";
+ case t_base_type::TYPE_STRING:
+ return "(GDestroyNotify) g_ptr_array_unref";
+ default:
+ throw "compiler error: no array info for type";
+ }
+ } else if (etype->is_container() || etype->is_struct()) {
+ return "(GDestroyNotify) g_ptr_array_unref";
+ ;
+ } else if (etype->is_enum()) {
+ return "(GDestroyNotify) g_array_unref";
+ }
+ printf("Type not expected inside the array: %s\n", etype->get_name().c_str());
+ throw "Type not expected inside array";
+ } else if (ttype->is_typedef()) {
+ return generate_free_func_from_type(((t_typedef*)ttype)->get_type());
+ }
+ printf("Type not expected: %s\n", ttype->get_name().c_str());
+ throw "Type not expected";
+}
+
+string t_c_glib_generator::generate_hash_func_from_type(t_type* ttype) {
+ if (ttype == NULL)
+ return "NULL";
+
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot determine hash type";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "thrift_boolean_hash";
+ case t_base_type::TYPE_I8:
+ return "thrift_int8_hash";
+ case t_base_type::TYPE_I16:
+ return "thrift_int16_hash";
+ case t_base_type::TYPE_I32:
+ return "g_int_hash";
+ case t_base_type::TYPE_I64:
+ return "g_int64_hash";
+ case t_base_type::TYPE_DOUBLE:
+ return "g_double_hash";
+ case t_base_type::TYPE_STRING:
+ return "g_str_hash";
+ default:
+ throw "compiler error: no hash table info for type";
+ }
+ } else if (ttype->is_enum()) {
+ return "g_direct_hash";
+ } else if (ttype->is_container() || ttype->is_struct()) {
+ return "g_direct_hash";
+ } else if (ttype->is_typedef()) {
+ return generate_hash_func_from_type(((t_typedef*)ttype)->get_type());
+ }
+ printf("Type not expected: %s\n", ttype->get_name().c_str());
+ throw "Type not expected";
+}
+
+string t_c_glib_generator::generate_cmp_func_from_type(t_type* ttype) {
+ if (ttype == NULL)
+ return "NULL";
+
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot determine hash type";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "thrift_boolean_equal";
+ case t_base_type::TYPE_I8:
+ return "thrift_int8_equal";
+ case t_base_type::TYPE_I16:
+ return "thrift_int16_equal";
+ case t_base_type::TYPE_I32:
+ return "g_int_equal";
+ case t_base_type::TYPE_I64:
+ return "g_int64_equal";
+ case t_base_type::TYPE_DOUBLE:
+ return "g_double_equal";
+ case t_base_type::TYPE_STRING:
+ return "g_str_equal";
+ default:
+ throw "compiler error: no hash table info for type";
+ }
+ } else if (ttype->is_enum()) {
+ return "g_direct_equal";
+ } else if (ttype->is_container() || ttype->is_struct()) {
+ return "g_direct_equal";
+ } else if (ttype->is_typedef()) {
+ return generate_cmp_func_from_type(((t_typedef*)ttype)->get_type());
+ }
+ printf("Type not expected: %s\n", ttype->get_name().c_str());
+ throw "Type not expected";
+}
+
+string t_c_glib_generator::generate_new_hash_from_type(t_type* key, t_type* value) {
+ string hash_func = generate_hash_func_from_type(key);
+ string cmp_func = generate_cmp_func_from_type(key);
+ string key_free_func = generate_free_func_from_type(key);
+ string value_free_func = generate_free_func_from_type(value);
+
+ return "g_hash_table_new_full (" + hash_func + ", " + cmp_func + ", " + key_free_func + ", "
+ + value_free_func + ");";
+}
+
+string t_c_glib_generator::generate_new_array_from_type(t_type* ttype) {
+ if (ttype->is_void()) {
+ throw std::runtime_error("compiler error: cannot determine array type");
+ } else if (is_numeric(ttype)) {
+ return "g_array_new (0, 1, sizeof (" + base_type_name(ttype) + "));";
+ } else {
+ string free_func = generate_free_func_from_type(ttype);
+ return "g_ptr_array_new_with_free_func (" + free_func + ");";
+ }
+}
+
+/***************************************
+ * UTILITY FUNCTIONS *
+ ***************************************/
+
+/**
+ * Upper case a string.
+ */
+string to_upper_case(string name) {
+ string s(name);
+ std::transform(s.begin(), s.end(), s.begin(), ::toupper);
+ return s;
+}
+
+/**
+ * Lower case a string.
+ */
+string to_lower_case(string name) {
+ string s(name);
+ std::transform(s.begin(), s.end(), s.begin(), ::tolower);
+ return s;
+}
+
+/**
+ * Makes a string friendly to C code standards by lowercasing and adding
+ * underscores, with the exception of the first character. For example:
+ *
+ * Input: "ZomgCamelCase"
+ * Output: "zomg_camel_case"
+ */
+string initial_caps_to_underscores(string name) {
+ string ret;
+ const char* tmp = name.c_str();
+ int pos = 0;
+
+ /* the first character isn't underscored if uppercase, just lowercased */
+ ret += tolower(tmp[pos]);
+ pos++;
+ for (unsigned int i = pos; i < name.length(); i++) {
+ char lc = tolower(tmp[i]);
+ if (lc != tmp[i]) {
+ ret += '_';
+ }
+ ret += lc;
+ }
+
+ return ret;
+}
+
+/**
+ * Performs the reverse operation of initial_caps_to_underscores: The first
+ * character of the string is made uppercase, along with each character that
+ * follows an underscore (which is removed). Useful for converting Thrift
+ * service-method names into GObject-style class names.
+ *
+ * Input: "zomg_camel_case"
+ * Output: "ZomgCamelCase"
+ */
+string underscores_to_initial_caps(string name) {
+ string ret;
+ const char* tmp = name.c_str();
+ bool uppercase_next = true;
+
+ for (unsigned int i = 0; i < name.length(); i++) {
+ char c = tmp[i];
+ if (c == '_') {
+ uppercase_next = true;
+ } else {
+ if (uppercase_next) {
+ ret += toupper(c);
+ uppercase_next = false;
+ } else {
+ ret += c;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* register this generator with the main program */
+THRIFT_REGISTER_GENERATOR(c_glib, "C, using GLib", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cl_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cl_generator.cc
new file mode 100644
index 000000000..ad7c0ef17
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cl_generator.cc
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2008- Patrick Collison <patrick@collison.ie>
+ * Copyright (c) 2006- Facebook
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include <string>
+#include <algorithm>
+
+#include "thrift/platform.h"
+#include "t_oop_generator.h"
+
+using namespace std;
+
+
+/**
+ * Common Lisp code generator.
+ *
+ * @author Patrick Collison <patrick@collison.ie>
+ */
+class t_cl_generator : public t_oop_generator {
+ public:
+ t_cl_generator(
+ t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program)
+ {
+ no_asd = false;
+ system_prefix = "thrift-gen-";
+
+ std::map<std::string, std::string>::const_iterator iter;
+
+ for(iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if(iter->first.compare("no_asd") == 0) {
+ no_asd = true;
+ } else if (iter->first.compare("sys_pref") == 0) {
+ system_prefix = iter->second;
+ } else {
+ throw "unknown option cl:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-cl";
+ copy_options_ = option_string;
+ }
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_typedef (t_typedef* ttypedef) override;
+ void generate_enum (t_enum* tenum) override;
+ void generate_const (t_const* tconst) override;
+ void generate_struct (t_struct* tstruct) override;
+ void generate_xception (t_struct* txception) override;
+ void generate_service (t_service* tservice) override;
+ void generate_cl_struct (std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_cl_struct_internal (std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_exception_sig(std::ostream& out, t_function* f);
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ std::string cl_autogen_comment();
+ void asdf_def(std::ostream &out);
+ void package_def(std::ostream &out);
+ void package_in(std::ostream &out);
+ std::string generated_package();
+ std::string prefix(std::string name);
+ std::string package_of(t_program* program);
+ std::string package();
+ std::string render_includes();
+
+ std::string type_name(t_type* ttype);
+ std::string typespec (t_type *t);
+ std::string function_signature(t_function* tfunction);
+ std::string argument_list(t_struct* tstruct);
+
+ std::string cl_docstring(std::string raw);
+
+ private:
+
+ int temporary_var;
+ /**
+ * Isolate the variable definitions, as they can require structure definitions
+ */
+ ofstream_with_content_based_conditional_update f_asd_;
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_vars_;
+
+ std::string copy_options_;
+
+ bool no_asd;
+ std::string system_prefix;
+};
+
+
+void t_cl_generator::init_generator() {
+ MKDIR(get_out_dir().c_str());
+ string program_dir = get_out_dir() + "/" + program_name_;
+ MKDIR(program_dir.c_str());
+
+ temporary_var = 0;
+
+ string f_types_name = program_dir + "/" + program_name_ + "-types.lisp";
+ string f_vars_name = program_dir + "/" + program_name_ + "-vars.lisp";
+
+ f_types_.open(f_types_name);
+ f_types_ << cl_autogen_comment() << endl;
+ f_vars_.open(f_vars_name);
+ f_vars_ << cl_autogen_comment() << endl;
+
+ package_def(f_types_);
+ package_in(f_types_);
+ package_in(f_vars_);
+
+ if (!no_asd) {
+ string f_asd_name = program_dir + "/" + system_prefix + program_name_ + ".asd";
+ f_asd_.open(f_asd_name);
+ f_asd_ << cl_autogen_comment() << endl;
+ asdf_def(f_asd_);
+ }
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_cl_generator::render_includes() {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ result += ":depends-on (:thrift";
+ for (auto include : includes) {
+ result += " :" + system_prefix + underscore(include->get_name());
+ }
+ result += ")\n";
+ return result;
+}
+
+string t_cl_generator::package_of(t_program* program) {
+ string prefix = program->get_namespace("cl");
+ return prefix.empty() ? "thrift-generated" : prefix;
+}
+
+string t_cl_generator::package() {
+ return package_of(program_);
+}
+
+string t_cl_generator::prefix(string symbol) {
+ return "\"" + symbol + "\"";
+}
+
+string t_cl_generator::cl_autogen_comment() {
+ return
+ std::string(";;; ") + "Autogenerated by Thrift\n" +
+ ";;; DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" +
+ ";;; options string: " + copy_options_ + "\n";
+}
+
+string t_cl_generator::cl_docstring(string raw) {
+ replace(raw.begin(), raw.end(), '"', '\'');
+ return raw;
+}
+
+
+void t_cl_generator::close_generator() {
+ f_asd_.close();
+ f_types_.close();
+ f_vars_.close();
+}
+
+string t_cl_generator::generated_package() {
+ return program_->get_namespace("cpp");
+}
+
+void t_cl_generator::asdf_def(std::ostream &out) {
+ out << "(asdf:defsystem #:" << system_prefix << program_name_ << endl;
+ indent_up();
+ out << indent() << render_includes()
+ << indent() << ":serial t" << endl
+ << indent() << ":components ("
+ << "(:file \"" << program_name_ << "-types\") "
+ << "(:file \"" << program_name_ << "-vars\")))" << endl;
+ indent_down();
+}
+
+/***
+ * Generate a package definition. Add use references equivalent to the idl file's include statements.
+ */
+void t_cl_generator::package_def(std::ostream &out) {
+ const vector<t_program*>& includes = program_->get_includes();
+
+ out << "(thrift:def-package :" << package();
+ if ( includes.size() > 0 ) {
+ out << " :use (";
+ for (auto include : includes) {
+ out << " :" << include->get_name();
+ }
+ out << ")";
+ }
+ out << ")" << endl << endl;
+}
+
+void t_cl_generator::package_in(std::ostream &out) {
+ out << "(cl:in-package :" << package() << ")" << endl << endl;
+}
+
+/**
+ * Generates a typedef. This is not done in Common Lisp, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_cl_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+void t_cl_generator::generate_enum(t_enum* tenum) {
+ f_types_ << "(thrift:def-enum " << prefix(tenum->get_name()) << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ int value = -1;
+
+ indent_up();
+ f_types_ << indent() << "(";
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ value = (*c_iter)->get_value();
+
+ if(c_iter != constants.begin()) f_types_ << endl << indent() << " ";
+
+ f_types_ << "(\"" << (*c_iter)->get_name() << "\" . " << value << ")";
+ }
+ indent_down();
+ f_types_ << "))" << endl << endl;
+}
+
+/**
+ * Generate a constant value
+ */
+void t_cl_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ f_vars_ << "(thrift:def-constant " << prefix(name) << " " << render_const_value(type, value) << ")"
+ << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_cl_generator::render_const_value(t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream out;
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << "\"" << value->get_string() << "\"";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "t" : "nil");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ indent(out) << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << (type->is_struct() ? "(make-instance '" : "(make-exception '") <<
+ lowercase(type->get_name()) << " " << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ out << indent() << ":" << v_iter->first->get_string() << " " <<
+ render_const_value(field_type, v_iter->second) << endl;
+ }
+ out << indent() << ")";
+
+ indent_down();
+ } else if (type->is_map()) {
+ // emit an hash form with both keys and values to be evaluated
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "(thrift:map ";
+ indent_up();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << endl << indent()
+ << "(cl:cons " << render_const_value(ktype, v_iter->first) << " "
+ << render_const_value(vtype, v_iter->second) << ")";
+ }
+ indent_down();
+ out << indent() << ")";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ if (type->is_set()) {
+ out << "(thrift:set" << endl;
+ } else {
+ out << "(thrift:list" << endl;
+ }
+ indent_up();
+ indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << render_const_value(etype, *v_iter) << endl;
+ }
+ out << indent() << ")";
+ indent_down();
+ indent_down();
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+ return out.str();
+}
+
+void t_cl_generator::generate_struct(t_struct* tstruct) {
+ generate_cl_struct(f_types_, tstruct, false);
+}
+
+void t_cl_generator::generate_xception(t_struct* txception) {
+ generate_cl_struct(f_types_, txception, true);
+}
+
+void t_cl_generator::generate_cl_struct_internal(std::ostream& out, t_struct* tstruct, bool is_exception) {
+ (void)is_exception;
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << "(";
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_const_value* value = (*m_iter)->get_value();
+ t_type* type = (*m_iter)->get_type();
+
+ if (m_iter != members.begin()) {
+ out << endl << indent() << " ";
+ }
+ out << "(" << prefix((*m_iter)->get_name()) << " " <<
+ ( (NULL != value) ? render_const_value(type, value) : "nil" ) <<
+ " :id " << (*m_iter)->get_key();
+ if ( type->is_base_type() && "string" == typespec(type) )
+ if ( ((t_base_type*)type)->is_binary() )
+ out << " :type binary";
+ else
+ out << " :type string";
+ else
+ out << " :type " << typespec(type);
+ if ( (*m_iter)->get_req() == t_field::T_OPTIONAL ) {
+ out << " :optional t";
+ }
+ if ( (*m_iter)->has_doc()) {
+ out << " :documentation \"" << cl_docstring((*m_iter)->get_doc()) << "\"";
+ }
+ out <<")";
+ }
+
+ out << ")";
+}
+
+void t_cl_generator::generate_cl_struct(std::ostream& out, t_struct* tstruct, bool is_exception = false) {
+ std::string name = type_name(tstruct);
+ out << (is_exception ? "(thrift:def-exception " : "(thrift:def-struct ") <<
+ prefix(name) << endl;
+ indent_up();
+ if ( tstruct->has_doc() ) {
+ out << indent() ;
+ out << "\"" << cl_docstring(tstruct->get_doc()) << "\"" << endl;
+ }
+ out << indent() ;
+ generate_cl_struct_internal(out, tstruct, is_exception);
+ indent_down();
+ out << ")" << endl << endl;
+}
+
+void t_cl_generator::generate_exception_sig(std::ostream& out, t_function* f) {
+ generate_cl_struct_internal(out, f->get_xceptions(), true);
+}
+
+void t_cl_generator::generate_service(t_service* tservice) {
+ string extends_client;
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ if (tservice->get_extends() != NULL) {
+ extends_client = type_name(tservice->get_extends());
+ }
+
+ extends_client = extends_client.empty() ? "nil" : prefix(extends_client);
+
+ f_types_ << "(thrift:def-service " << prefix(service_name_) << " "
+ << extends_client;
+
+ indent_up();
+
+ if ( tservice->has_doc()) {
+ f_types_ << endl << indent()
+ << "(:documentation \"" << cl_docstring(tservice->get_doc()) << "\")";
+ }
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_function* function = *f_iter;
+ string fname = function->get_name();
+ string signature = function_signature(function);
+ t_struct* exceptions = function->get_xceptions();
+ const vector<t_field*>& xmembers = exceptions->get_members();
+
+ f_types_ << endl << indent() << "(:method " << prefix(fname);
+ f_types_ << " (" << signature << " " << typespec((*f_iter)->get_returntype()) << ")";
+ if (xmembers.size() > 0) {
+ f_types_ << endl << indent() << " :exceptions " ;
+ generate_exception_sig(f_types_, function);
+ }
+ if ( (*f_iter)->is_oneway() ) {
+ f_types_ << endl << indent() << " :oneway t";
+ }
+ if ( (*f_iter)->has_doc() ) {
+ f_types_ << endl << indent() << " :documentation \""
+ << cl_docstring((*f_iter)->get_doc()) << "\"";
+ }
+ f_types_ << ")";
+ }
+
+ f_types_ << ")" << endl << endl;
+
+ indent_down();
+}
+
+string t_cl_generator::typespec(t_type *t) {
+ t = get_true_type(t);
+
+ if (t -> is_binary()){
+ return "binary";
+ } else if (t->is_base_type()) {
+ return type_name(t);
+ } else if (t->is_map()) {
+ t_map *m = (t_map*) t;
+ return "(thrift:map " + typespec(m->get_key_type()) + " " +
+ typespec(m->get_val_type()) + ")";
+ } else if (t->is_struct() || t->is_xception()) {
+ return "(struct " + prefix(type_name(t)) + ")";
+ } else if (t->is_list()) {
+ return "(thrift:list " + typespec(((t_list*) t)->get_elem_type()) + ")";
+ } else if (t->is_set()) {
+ return "(thrift:set " + typespec(((t_set*) t)->get_elem_type()) + ")";
+ } else if (t->is_enum()) {
+ return "(enum \"" + ((t_enum*) t)->get_name() + "\")";
+ } else {
+ throw "Sorry, I don't know how to generate this: " + type_name(t);
+ }
+}
+
+string t_cl_generator::function_signature(t_function* tfunction) {
+ return argument_list(tfunction->get_arglist());
+}
+
+string t_cl_generator::argument_list(t_struct* tstruct) {
+ stringstream res;
+ res << "(";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ res << " ";
+ }
+ res << "(" + prefix((*f_iter)->get_name()) << " " <<
+ typespec((*f_iter)->get_type()) << " " <<
+ (*f_iter)->get_key() << ")";
+
+
+ }
+ res << ")";
+ return res.str();
+}
+
+string t_cl_generator::type_name(t_type* ttype) {
+ string prefix = "";
+ t_program* program = ttype->get_program();
+
+ if (program != NULL && program != program_)
+ prefix = package_of(program) == package() ? "" : package_of(program) + ":";
+
+ string name = ttype->get_name();
+
+ if (ttype->is_struct() || ttype->is_xception())
+ name = lowercase(ttype->get_name());
+
+ return prefix + name;
+}
+
+THRIFT_REGISTER_GENERATOR(
+ cl,
+ "Common Lisp",
+ " no_asd: Do not define ASDF systems for each generated Thrift program.\n"
+ " sys_pref= The prefix to give ASDF system names. Default: thrift-gen-\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
new file mode 100644
index 000000000..d66b6e6e1
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
@@ -0,0 +1,4554 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <sys/stat.h>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::string;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * C++ code generator. This is legitimacy incarnate.
+ *
+ */
+class t_cpp_generator : public t_oop_generator {
+public:
+ t_cpp_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+
+ gen_pure_enums_ = false;
+ use_include_prefix_ = false;
+ gen_cob_style_ = false;
+ gen_no_client_completion_ = false;
+ gen_no_default_operators_ = false;
+ gen_templates_ = false;
+ gen_templates_only_ = false;
+ gen_moveable_ = false;
+ gen_no_ostream_operators_ = false;
+ gen_no_skeleton_ = false;
+
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("pure_enums") == 0) {
+ gen_pure_enums_ = true;
+ } else if( iter->first.compare("include_prefix") == 0) {
+ use_include_prefix_ = true;
+ } else if( iter->first.compare("cob_style") == 0) {
+ gen_cob_style_ = true;
+ } else if( iter->first.compare("no_client_completion") == 0) {
+ gen_no_client_completion_ = true;
+ } else if( iter->first.compare("no_default_operators") == 0) {
+ gen_no_default_operators_ = true;
+ } else if( iter->first.compare("templates") == 0) {
+ gen_templates_ = true;
+ gen_templates_only_ = (iter->second == "only");
+ } else if( iter->first.compare("moveable_types") == 0) {
+ gen_moveable_ = true;
+ } else if ( iter->first.compare("no_ostream_operators") == 0) {
+ gen_no_ostream_operators_ = true;
+ } else if ( iter->first.compare("no_skeleton") == 0) {
+ gen_no_skeleton_ = true;
+ } else {
+ throw "unknown option cpp:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-cpp";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_enum_ostream_operator_decl(std::ostream& out, t_enum* tenum);
+ void generate_enum_ostream_operator(std::ostream& out, t_enum* tenum);
+ void generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum);
+ void generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum);
+ void generate_forward_declaration(t_struct* tstruct) override;
+ void generate_struct(t_struct* tstruct) override { generate_cpp_struct(tstruct, false); }
+ void generate_xception(t_struct* txception) override { generate_cpp_struct(txception, true); }
+ void generate_cpp_struct(t_struct* tstruct, bool is_exception);
+
+ void generate_service(t_service* tservice) override;
+
+ void print_const_value(std::ostream& out, std::string name, t_type* type, t_const_value* value);
+ std::string render_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+
+ void generate_struct_declaration(std::ostream& out,
+ t_struct* tstruct,
+ bool is_exception = false,
+ bool pointers = false,
+ bool read = true,
+ bool write = true,
+ bool swap = false,
+ bool is_user_struct = false);
+ void generate_struct_definition(std::ostream& out,
+ std::ostream& force_cpp_out,
+ t_struct* tstruct,
+ bool setters = true,
+ bool is_user_struct = false);
+ void generate_copy_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_move_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_constructor_helper(std::ostream& out,
+ t_struct* tstruct,
+ bool is_excpetion,
+ bool is_move);
+ void generate_assignment_operator(std::ostream& out, t_struct* tstruct);
+ void generate_move_assignment_operator(std::ostream& out, t_struct* tstruct);
+ void generate_assignment_helper(std::ostream& out, t_struct* tstruct, bool is_move);
+ void generate_struct_reader(std::ostream& out, t_struct* tstruct, bool pointers = false);
+ void generate_struct_writer(std::ostream& out, t_struct* tstruct, bool pointers = false);
+ void generate_struct_result_writer(std::ostream& out, t_struct* tstruct, bool pointers = false);
+ void generate_struct_swap(std::ostream& out, t_struct* tstruct);
+ void generate_struct_print_method(std::ostream& out, t_struct* tstruct);
+ void generate_exception_what_method(std::ostream& out, t_struct* tstruct);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_interface(t_service* tservice, string style);
+ void generate_service_interface_factory(t_service* tservice, string style);
+ void generate_service_null(t_service* tservice, string style);
+ void generate_service_multiface(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice, string style);
+ void generate_service_processor(t_service* tservice, string style);
+ void generate_service_skeleton(t_service* tservice);
+ void generate_process_function(t_service* tservice,
+ t_function* tfunction,
+ string style,
+ bool specialized = false);
+ void generate_function_helpers(t_service* tservice, t_function* tfunction);
+ void generate_service_async_skeleton(t_service* tservice);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ std::string suffix = "");
+
+ void generate_deserialize_struct(std::ostream& out,
+ t_struct* tstruct,
+ std::string prefix = "",
+ bool pointer = false);
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix,
+ bool push_back,
+ std::string index);
+
+ void generate_serialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ std::string suffix = "");
+
+ void generate_serialize_struct(std::ostream& out,
+ t_struct* tstruct,
+ std::string prefix = "",
+ bool pointer = false);
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out, t_map* tmap, std::string iter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_function_call(ostream& out,
+ t_function* tfunction,
+ string target,
+ string iface,
+ string arg_prefix);
+ /*
+ * Helper rendering functions
+ */
+
+ std::string namespace_prefix(std::string ns);
+ std::string namespace_open(std::string ns);
+ std::string namespace_close(std::string ns);
+ std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false);
+ std::string base_type_name(t_base_type::t_base tbase);
+ std::string declare_field(t_field* tfield,
+ bool init = false,
+ bool pointer = false,
+ bool constant = false,
+ bool reference = false);
+ std::string function_signature(t_function* tfunction,
+ std::string style,
+ std::string prefix = "",
+ bool name_params = true);
+ std::string cob_function_signature(t_function* tfunction,
+ std::string prefix = "",
+ bool name_params = true);
+ std::string argument_list(t_struct* tstruct, bool name_params = true, bool start_comma = false);
+ std::string type_to_enum(t_type* ttype);
+
+ void generate_enum_constant_list(std::ostream& f,
+ const vector<t_enum_value*>& constants,
+ const char* prefix,
+ const char* suffix,
+ bool include_values);
+
+ void generate_struct_ostream_operator_decl(std::ostream& f, t_struct* tstruct);
+ void generate_struct_ostream_operator(std::ostream& f, t_struct* tstruct);
+ void generate_struct_print_method_decl(std::ostream& f, t_struct* tstruct);
+ void generate_exception_what_method_decl(std::ostream& f,
+ t_struct* tstruct,
+ bool external = false);
+
+ bool is_reference(t_field* tfield) { return tfield->get_reference(); }
+
+ bool is_complex_type(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception()
+ || (ttype->is_base_type()
+ && (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING));
+ }
+
+ void set_use_include_prefix(bool use_include_prefix) { use_include_prefix_ = use_include_prefix; }
+
+ /**
+ * The compiler option "no_thrift_ostream_impl" can be used to prevent
+ * the compiler from emitting implementations for operator <<. In this
+ * case the consuming application must provide any needed to build.
+ *
+ * To disable this on a per structure bases, one can alternatively set
+ * the annotation "cpp.customostream" to prevent thrift from emitting an
+ * operator << (std::ostream&).
+ *
+ * See AnnotationTest for validation of this annotation feature.
+ */
+ bool has_custom_ostream(t_type* ttype) const {
+ return (gen_no_ostream_operators_) ||
+ (ttype->annotations_.find("cpp.customostream") != ttype->annotations_.end());
+ }
+
+private:
+ /**
+ * Returns the include prefix to use for a file generated by program, or the
+ * empty string if no include prefix should be used.
+ */
+ std::string get_include_prefix(const t_program& program) const;
+
+ /**
+ * True if we should generate pure enums for Thrift enums, instead of wrapper classes.
+ */
+ bool gen_pure_enums_;
+
+ /**
+ * True if we should generate templatized reader/writer methods.
+ */
+ bool gen_templates_;
+
+ /**
+ * True iff we should generate process function pointers for only templatized
+ * reader/writer methods.
+ */
+ bool gen_templates_only_;
+
+ /**
+ * True if we should generate move constructors & assignment operators.
+ */
+ bool gen_moveable_;
+
+ /**
+ * True if we should generate ostream definitions
+ */
+ bool gen_no_ostream_operators_;
+
+ /**
+ * True iff we should use a path prefix in our #include statements for other
+ * thrift-generated header files.
+ */
+ bool use_include_prefix_;
+
+ /**
+ * True if we should generate "Continuation OBject"-style classes as well.
+ */
+ bool gen_cob_style_;
+
+ /**
+ * True if we should omit calls to completion__() in CobClient class.
+ */
+ bool gen_no_client_completion_;
+
+ /**
+ * True if we should omit generating the default opeartors ==, != and <.
+ */
+ bool gen_no_default_operators_;
+
+ /**
+ * True if we should generate skeleton.
+ */
+ bool gen_no_skeleton_;
+
+ /**
+ * Strings for namespace, computed once up front then used directly
+ */
+
+ std::string ns_open_;
+ std::string ns_close_;
+
+ /**
+ * File streams, stored here to avoid passing them as parameters to every
+ * function.
+ */
+
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_types_impl_;
+ ofstream_with_content_based_conditional_update f_types_tcc_;
+ ofstream_with_content_based_conditional_update f_header_;
+ ofstream_with_content_based_conditional_update f_service_;
+ ofstream_with_content_based_conditional_update f_service_tcc_;
+
+ // The ProcessorGenerator is used to generate parts of the code,
+ // so it needs access to many of our protected members and methods.
+ //
+ // TODO: The code really should be cleaned up so that helper methods for
+ // writing to the output files are separate from the generator classes
+ // themselves.
+ friend class ProcessorGenerator;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ */
+void t_cpp_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ // Make output file
+ string f_types_name = get_out_dir() + program_name_ + "_types.h";
+ f_types_.open(f_types_name);
+
+ string f_types_impl_name = get_out_dir() + program_name_ + "_types.cpp";
+ f_types_impl_.open(f_types_impl_name.c_str());
+
+ if (gen_templates_) {
+ // If we don't open the stream, it appears to just discard data,
+ // which is fine.
+ string f_types_tcc_name = get_out_dir() + program_name_ + "_types.tcc";
+ f_types_tcc_.open(f_types_tcc_name.c_str());
+ }
+
+ // Print header
+ f_types_ << autogen_comment();
+ f_types_impl_ << autogen_comment();
+ f_types_tcc_ << autogen_comment();
+
+ // Start ifndef
+ f_types_ << "#ifndef " << program_name_ << "_TYPES_H" << endl << "#define " << program_name_
+ << "_TYPES_H" << endl << endl;
+ f_types_tcc_ << "#ifndef " << program_name_ << "_TYPES_TCC" << endl << "#define " << program_name_
+ << "_TYPES_TCC" << endl << endl;
+
+ // Include base types
+ f_types_ << "#include <iosfwd>" << endl
+ << endl
+ << "#include <thrift/Thrift.h>" << endl
+ << "#include <thrift/TApplicationException.h>" << endl
+ << "#include <thrift/TBase.h>" << endl
+ << "#include <thrift/protocol/TProtocol.h>" << endl
+ << "#include <thrift/transport/TTransport.h>" << endl
+ << endl;
+ // Include C++xx compatibility header
+ f_types_ << "#include <functional>" << endl;
+ f_types_ << "#include <memory>" << endl;
+
+ // Include other Thrift includes
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ f_types_ << "#include \"" << get_include_prefix(*include) << include->get_name()
+ << "_types.h\"" << endl;
+
+ // XXX(simpkins): If gen_templates_ is enabled, we currently assume all
+ // included files were also generated with templates enabled.
+ f_types_tcc_ << "#include \"" << get_include_prefix(*include) << include->get_name()
+ << "_types.tcc\"" << endl;
+ }
+ f_types_ << endl;
+
+ // Include custom headers
+ const vector<string>& cpp_includes = program_->get_cpp_includes();
+ for (const auto & cpp_include : cpp_includes) {
+ if (cpp_include[0] == '<') {
+ f_types_ << "#include " << cpp_include << endl;
+ } else {
+ f_types_ << "#include \"" << cpp_include << "\"" << endl;
+ }
+ }
+ f_types_ << endl;
+
+ // Include the types file
+ f_types_impl_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
+ << "_types.h\"" << endl << endl;
+ f_types_tcc_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
+ << "_types.h\"" << endl << endl;
+
+ // The swap() code needs <algorithm> for std::swap()
+ f_types_impl_ << "#include <algorithm>" << endl;
+ // for operator<<
+ f_types_impl_ << "#include <ostream>" << endl << endl;
+ f_types_impl_ << "#include <thrift/TToString.h>" << endl << endl;
+
+ // Open namespace
+ ns_open_ = namespace_open(program_->get_namespace("cpp"));
+ ns_close_ = namespace_close(program_->get_namespace("cpp"));
+
+ f_types_ << ns_open_ << endl << endl;
+
+ f_types_impl_ << ns_open_ << endl << endl;
+
+ f_types_tcc_ << ns_open_ << endl << endl;
+}
+
+/**
+ * Closes the output files.
+ */
+void t_cpp_generator::close_generator() {
+ // Close namespace
+ f_types_ << ns_close_ << endl << endl;
+ f_types_impl_ << ns_close_ << endl;
+ f_types_tcc_ << ns_close_ << endl << endl;
+
+ // Include the types.tcc file from the types header file,
+ // so clients don't have to explicitly include the tcc file.
+ // TODO(simpkins): Make this a separate option.
+ if (gen_templates_) {
+ f_types_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
+ << "_types.tcc\"" << endl << endl;
+ }
+
+ // Close ifndef
+ f_types_ << "#endif" << endl;
+ f_types_tcc_ << "#endif" << endl;
+
+ // Close output file
+ f_types_.close();
+ f_types_impl_.close();
+ f_types_tcc_.close();
+}
+
+/**
+ * Generates a typedef. This is just a simple 1-liner in C++
+ *
+ * @param ttypedef The type definition
+ */
+void t_cpp_generator::generate_typedef(t_typedef* ttypedef) {
+ f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " "
+ << ttypedef->get_symbolic() << ";" << endl << endl;
+}
+
+void t_cpp_generator::generate_enum_constant_list(std::ostream& f,
+ const vector<t_enum_value*>& constants,
+ const char* prefix,
+ const char* suffix,
+ bool include_values) {
+ f << " {" << endl;
+ indent_up();
+
+ vector<t_enum_value*>::const_iterator c_iter;
+ bool first = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f << "," << endl;
+ }
+ indent(f) << prefix << (*c_iter)->get_name() << suffix;
+ if (include_values) {
+ f << " = " << (*c_iter)->get_value();
+ }
+ }
+
+ f << endl;
+ indent_down();
+ indent(f) << "};" << endl;
+}
+
+/**
+ * Generates code for an enumerated type. In C++, this is essentially the same
+ * as the thrift definition itself, using the enum keyword in C++.
+ *
+ * @param tenum The enumeration
+ */
+void t_cpp_generator::generate_enum(t_enum* tenum) {
+ vector<t_enum_value*> constants = tenum->get_constants();
+
+ std::string enum_name = tenum->get_name();
+ if (!gen_pure_enums_) {
+ enum_name = "type";
+ f_types_ << indent() << "struct " << tenum->get_name() << " {" << endl;
+ indent_up();
+ }
+ f_types_ << indent() << "enum " << enum_name;
+
+ generate_enum_constant_list(f_types_, constants, "", "", true);
+
+ if (!gen_pure_enums_) {
+ indent_down();
+ f_types_ << "};" << endl;
+ }
+
+ f_types_ << endl;
+
+ /**
+ Generate a character array of enum names for debugging purposes.
+ */
+ std::string prefix = "";
+ if (!gen_pure_enums_) {
+ prefix = tenum->get_name() + "::";
+ }
+
+ f_types_impl_ << indent() << "int _k" << tenum->get_name() << "Values[] =";
+ generate_enum_constant_list(f_types_impl_, constants, prefix.c_str(), "", false);
+
+ f_types_impl_ << indent() << "const char* _k" << tenum->get_name() << "Names[] =";
+ generate_enum_constant_list(f_types_impl_, constants, "\"", "\"", false);
+
+ f_types_ << indent() << "extern const std::map<int, const char*> _" << tenum->get_name()
+ << "_VALUES_TO_NAMES;" << endl << endl;
+
+ f_types_impl_ << indent() << "const std::map<int, const char*> _" << tenum->get_name()
+ << "_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(" << constants.size() << ", _k"
+ << tenum->get_name() << "Values"
+ << ", _k" << tenum->get_name() << "Names), "
+ << "::apache::thrift::TEnumIterator(-1, NULL, NULL));" << endl << endl;
+
+ generate_enum_ostream_operator_decl(f_types_, tenum);
+ generate_enum_ostream_operator(f_types_impl_, tenum);
+
+ generate_enum_to_string_helper_function_decl(f_types_, tenum);
+ generate_enum_to_string_helper_function(f_types_impl_, tenum);
+}
+
+void t_cpp_generator::generate_enum_ostream_operator_decl(std::ostream& out, t_enum* tenum) {
+
+ out << "std::ostream& operator<<(std::ostream& out, const ";
+ if (gen_pure_enums_) {
+ out << tenum->get_name();
+ } else {
+ out << tenum->get_name() << "::type&";
+ }
+ out << " val);" << endl;
+ out << endl;
+}
+
+void t_cpp_generator::generate_enum_ostream_operator(std::ostream& out, t_enum* tenum) {
+
+ // If we've been told the consuming application will provide an ostream
+ // operator definition then we only make a declaration:
+
+ if (!has_custom_ostream(tenum)) {
+ out << "std::ostream& operator<<(std::ostream& out, const ";
+ if (gen_pure_enums_) {
+ out << tenum->get_name();
+ } else {
+ out << tenum->get_name() << "::type&";
+ }
+ out << " val) ";
+ scope_up(out);
+
+ out << indent() << "std::map<int, const char*>::const_iterator it = _"
+ << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << endl;
+ out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << endl;
+ indent_up();
+ out << indent() << "out << it->second;" << endl;
+ indent_down();
+ out << indent() << "} else {" << endl;
+ indent_up();
+ out << indent() << "out << static_cast<int>(val);" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+
+ out << indent() << "return out;" << endl;
+ scope_down(out);
+ out << endl;
+ }
+}
+
+void t_cpp_generator::generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum) {
+ out << "std::string to_string(const ";
+ if (gen_pure_enums_) {
+ out << tenum->get_name();
+ } else {
+ out << tenum->get_name() << "::type&";
+ }
+ out << " val);" << endl;
+ out << endl;
+}
+
+void t_cpp_generator::generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum) {
+ if (!has_custom_ostream(tenum)) {
+ out << "std::string to_string(const ";
+ if (gen_pure_enums_) {
+ out << tenum->get_name();
+ } else {
+ out << tenum->get_name() << "::type&";
+ }
+ out << " val) " ;
+ scope_up(out);
+
+ out << indent() << "std::map<int, const char*>::const_iterator it = _"
+ << tenum->get_name() << "_VALUES_TO_NAMES.find(val);" << endl;
+ out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << endl;
+ indent_up();
+ out << indent() << "return std::string(it->second);" << endl;
+ indent_down();
+ out << indent() << "} else {" << endl;
+ indent_up();
+ out << indent() << "return std::to_string(static_cast<int>(val));" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+
+ scope_down(out);
+ out << endl;
+ }
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_cpp_generator::generate_consts(std::vector<t_const*> consts) {
+ string f_consts_name = get_out_dir() + program_name_ + "_constants.h";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name);
+
+ string f_consts_impl_name = get_out_dir() + program_name_ + "_constants.cpp";
+ ofstream_with_content_based_conditional_update f_consts_impl;
+ f_consts_impl.open(f_consts_impl_name);
+
+ // Print header
+ f_consts << autogen_comment();
+ f_consts_impl << autogen_comment();
+
+ // Start ifndef
+ f_consts << "#ifndef " << program_name_ << "_CONSTANTS_H" << endl << "#define " << program_name_
+ << "_CONSTANTS_H" << endl << endl << "#include \"" << get_include_prefix(*get_program())
+ << program_name_ << "_types.h\"" << endl << endl << ns_open_ << endl << endl;
+
+ f_consts_impl << "#include \"" << get_include_prefix(*get_program()) << program_name_
+ << "_constants.h\"" << endl << endl << ns_open_ << endl << endl;
+
+ f_consts << "class " << program_name_ << "Constants {" << endl << " public:" << endl << " "
+ << program_name_ << "Constants();" << endl << endl;
+ indent_up();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ string name = (*c_iter)->get_name();
+ t_type* type = (*c_iter)->get_type();
+ f_consts << indent() << type_name(type) << " " << name << ";" << endl;
+ }
+ indent_down();
+ f_consts << "};" << endl;
+
+ f_consts_impl << "const " << program_name_ << "Constants g_" << program_name_ << "_constants;"
+ << endl << endl << program_name_ << "Constants::" << program_name_
+ << "Constants() {" << endl;
+ indent_up();
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ print_const_value(f_consts_impl,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value());
+ }
+ indent_down();
+ indent(f_consts_impl) << "}" << endl;
+
+ f_consts << endl << "extern const " << program_name_ << "Constants g_" << program_name_
+ << "_constants;" << endl << endl << ns_close_ << endl << endl << "#endif" << endl;
+ f_consts.close();
+
+ f_consts_impl << endl << ns_close_ << endl << endl;
+ f_consts_impl.close();
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+void t_cpp_generator::print_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ type = get_true_type(type);
+ if (type->is_base_type()) {
+ string v2 = render_const_value(out, name, type, value);
+ indent(out) << name << " = " << v2 << ";" << endl << endl;
+ } else if (type->is_enum()) {
+ indent(out) << name << " = (" << type_name(type) << ")" << value->get_integer() << ";" << endl
+ << endl;
+ } else if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ bool is_nonrequired_field = false;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ is_nonrequired_field = false;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ is_nonrequired_field = (*f_iter)->get_req() != t_field::T_REQUIRED;
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ indent(out) << name << "." << v_iter->first->get_string() << " = " << val << ";" << endl;
+ if (is_nonrequired_field) {
+ indent(out) << name << ".__isset." << v_iter->first->get_string() << " = true;" << endl;
+ }
+ }
+ out << endl;
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ indent(out) << name << ".insert(std::make_pair(" << key << ", " << val << "));" << endl;
+ }
+ out << endl;
+ } else if (type->is_list()) {
+ t_type* etype = ((t_list*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ indent(out) << name << ".push_back(" << val << ");" << endl;
+ }
+ out << endl;
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ indent(out) << name << ".insert(" << val << ");" << endl;
+ }
+ out << endl;
+ } else {
+ throw "INVALID TYPE IN print_const_value: " + type->get_name();
+ }
+}
+
+/**
+ *
+ */
+string t_cpp_generator::render_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ render << value->get_integer() << "LL";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << "static_cast<double>(" << value->get_integer() << ")";
+ } else {
+ render << emit_double_as_string(value->get_double());
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << "(" << type_name(type) << ")" << value->get_integer();
+ } else {
+ string t = tmp("tmp");
+ indent(out) << type_name(type) << " " << t << ";" << endl;
+ print_const_value(out, t, type, value);
+ render << t;
+ }
+
+ return render.str();
+}
+
+void t_cpp_generator::generate_forward_declaration(t_struct* tstruct) {
+ // Forward declare struct def
+ f_types_ << indent() << "class " << tstruct->get_name() << ";" << endl << endl;
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is a class
+ * with data members and a read/write() function, plus a mirroring isset
+ * inner class.
+ *
+ * @param tstruct The struct definition
+ */
+void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) {
+ generate_struct_declaration(f_types_, tstruct, is_exception, false, true, true, true, true);
+ generate_struct_definition(f_types_impl_, f_types_impl_, tstruct, true, true);
+
+ std::ostream& out = (gen_templates_ ? f_types_tcc_ : f_types_impl_);
+ generate_struct_reader(out, tstruct);
+ generate_struct_writer(out, tstruct);
+ generate_struct_swap(f_types_impl_, tstruct);
+ generate_copy_constructor(f_types_impl_, tstruct, is_exception);
+ if (gen_moveable_) {
+ generate_move_constructor(f_types_impl_, tstruct, is_exception);
+ }
+ generate_assignment_operator(f_types_impl_, tstruct);
+ if (gen_moveable_) {
+ generate_move_assignment_operator(f_types_impl_, tstruct);
+ }
+
+ if (!has_custom_ostream(tstruct)) {
+ generate_struct_print_method(f_types_impl_, tstruct);
+ }
+
+ if (is_exception) {
+ generate_exception_what_method(f_types_impl_, tstruct);
+ }
+}
+
+void t_cpp_generator::generate_copy_constructor(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/false);
+}
+
+void t_cpp_generator::generate_move_constructor(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/true);
+}
+
+namespace {
+// Helper to convert a variable to rvalue, if move is enabled
+std::string maybeMove(std::string const& other, bool move) {
+ if (move) {
+ return "std::move(" + other + ")";
+ }
+ return other;
+}
+}
+
+void t_cpp_generator::generate_constructor_helper(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_move) {
+
+ std::string tmp_name = tmp("other");
+
+ indent(out) << tstruct->get_name() << "::" << tstruct->get_name();
+
+ if (is_move) {
+ out << "( " << tstruct->get_name() << "&& ";
+ } else {
+ out << "(const " << tstruct->get_name() << "& ";
+ }
+ out << tmp_name << ") ";
+ if (is_exception)
+ out << ": TException() ";
+ out << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+
+ // eliminate compiler unused warning
+ if (members.empty())
+ indent(out) << "(void) " << tmp_name << ";" << endl;
+
+ vector<t_field*>::const_iterator f_iter;
+ bool has_nonrequired_fields = false;
+ for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) {
+ if ((*f_iter)->get_req() != t_field::T_REQUIRED)
+ has_nonrequired_fields = true;
+ indent(out) << (*f_iter)->get_name() << " = "
+ << maybeMove(tmp_name + "." + (*f_iter)->get_name(), is_move) << ";" << endl;
+ }
+
+ if (has_nonrequired_fields) {
+ indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_cpp_generator::generate_assignment_operator(ostream& out, t_struct* tstruct) {
+ generate_assignment_helper(out, tstruct, /*is_move=*/false);
+}
+
+void t_cpp_generator::generate_move_assignment_operator(ostream& out, t_struct* tstruct) {
+ generate_assignment_helper(out, tstruct, /*is_move=*/true);
+}
+
+void t_cpp_generator::generate_assignment_helper(ostream& out, t_struct* tstruct, bool is_move) {
+ std::string tmp_name = tmp("other");
+
+ indent(out) << tstruct->get_name() << "& " << tstruct->get_name() << "::operator=(";
+
+ if (is_move) {
+ out << tstruct->get_name() << "&& ";
+ } else {
+ out << "const " << tstruct->get_name() << "& ";
+ }
+ out << tmp_name << ") {" << endl;
+
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+
+ // eliminate compiler unused warning
+ if (members.empty())
+ indent(out) << "(void) " << tmp_name << ";" << endl;
+
+ vector<t_field*>::const_iterator f_iter;
+ bool has_nonrequired_fields = false;
+ for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) {
+ if ((*f_iter)->get_req() != t_field::T_REQUIRED)
+ has_nonrequired_fields = true;
+ indent(out) << (*f_iter)->get_name() << " = "
+ << maybeMove(tmp_name + "." + (*f_iter)->get_name(), is_move) << ";" << endl;
+ }
+ if (has_nonrequired_fields) {
+ indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl;
+ }
+
+ indent(out) << "return *this;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+/**
+ * Writes the struct declaration into the header file
+ *
+ * @param out Output stream
+ * @param tstruct The struct
+ */
+void t_cpp_generator::generate_struct_declaration(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool pointers,
+ bool read,
+ bool write,
+ bool swap,
+ bool is_user_struct) {
+ string extends = "";
+ if (is_exception) {
+ extends = " : public ::apache::thrift::TException";
+ } else {
+ if (is_user_struct && !gen_templates_) {
+ extends = " : public virtual ::apache::thrift::TBase";
+ }
+ }
+
+ // Get members
+ vector<t_field*>::const_iterator m_iter;
+ const vector<t_field*>& members = tstruct->get_members();
+
+ // Write the isset structure declaration outside the class. This makes
+ // the generated code amenable to processing by SWIG.
+ // We only declare the struct if it gets used in the class.
+
+ // Isset struct has boolean fields, but only for non-required fields.
+ bool has_nonrequired_fields = false;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED)
+ has_nonrequired_fields = true;
+ }
+
+ if (has_nonrequired_fields && (!pointers || read)) {
+
+ out << indent() << "typedef struct _" << tstruct->get_name() << "__isset {" << endl;
+ indent_up();
+
+ indent(out) << "_" << tstruct->get_name() << "__isset() ";
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() == t_field::T_REQUIRED) {
+ continue;
+ }
+ string isSet = ((*m_iter)->get_value() != NULL) ? "true" : "false";
+ if (first) {
+ first = false;
+ out << ": " << (*m_iter)->get_name() << "(" << isSet << ")";
+ } else {
+ out << ", " << (*m_iter)->get_name() << "(" << isSet << ")";
+ }
+ }
+ out << " {}" << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ indent(out) << "bool " << (*m_iter)->get_name() << " :1;" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "} _" << tstruct->get_name() << "__isset;" << endl;
+ }
+
+ out << endl;
+
+ // Open struct def
+ out << indent() << "class " << tstruct->get_name() << extends << " {" << endl << indent()
+ << " public:" << endl << endl;
+ indent_up();
+
+ if (!pointers) {
+ // Copy constructor
+ indent(out) << tstruct->get_name() << "(const " << tstruct->get_name() << "&);" << endl;
+
+ // Move constructor
+ if (gen_moveable_) {
+ indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&);" << endl;
+ }
+
+ // Assignment Operator
+ indent(out) << tstruct->get_name() << "& operator=(const " << tstruct->get_name() << "&);"
+ << endl;
+
+ // Move assignment operator
+ if (gen_moveable_) {
+ indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&);" << endl;
+ }
+
+ // Default constructor
+ indent(out) << tstruct->get_name() << "()";
+
+ bool init_ctor = false;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if (t->is_base_type() || t->is_enum() || is_reference(*m_iter)) {
+ string dval;
+ if (t->is_enum()) {
+ dval += "(" + type_name(t) + ")";
+ }
+ dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0";
+ t_const_value* cv = (*m_iter)->get_value();
+ if (cv != NULL) {
+ dval = render_const_value(out, (*m_iter)->get_name(), t, cv);
+ }
+ if (!init_ctor) {
+ init_ctor = true;
+ out << " : ";
+ out << (*m_iter)->get_name() << "(" << dval << ")";
+ } else {
+ out << ", " << (*m_iter)->get_name() << "(" << dval << ")";
+ }
+ }
+ }
+ out << " {" << endl;
+ indent_up();
+ // TODO(dreiss): When everything else in Thrift is perfect,
+ // do more of these in the initializer list.
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+
+ if (!t->is_base_type()) {
+ t_const_value* cv = (*m_iter)->get_value();
+ if (cv != NULL) {
+ print_const_value(out, (*m_iter)->get_name(), t, cv);
+ }
+ }
+ }
+ scope_down(out);
+ }
+
+ if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) {
+ out << endl << indent() << "virtual ~" << tstruct->get_name() << "() noexcept;" << endl;
+ }
+
+ // Declare all fields
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << declare_field(*m_iter,
+ false,
+ (pointers && !(*m_iter)->get_type()->is_xception()),
+ !read) << endl;
+ }
+
+ // Add the __isset data member if we need it, using the definition from above
+ if (has_nonrequired_fields && (!pointers || read)) {
+ out << endl << indent() << "_" << tstruct->get_name() << "__isset __isset;" << endl;
+ }
+
+ // Create a setter function for each field
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (pointers) {
+ continue;
+ }
+ if (is_reference((*m_iter))) {
+ out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(::std::shared_ptr<"
+ << type_name((*m_iter)->get_type(), false, false) << ">";
+ out << " val);" << endl;
+ } else {
+ out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "("
+ << type_name((*m_iter)->get_type(), false, true);
+ out << " val);" << endl;
+ }
+ }
+ out << endl;
+
+ if (!pointers) {
+ // Should we generate default operators?
+ if (!gen_no_default_operators_) {
+ // Generate an equality testing operator. Make it inline since the compiler
+ // will do a better job than we would when deciding whether to inline it.
+ out << indent() << "bool operator == (const " << tstruct->get_name() << " & "
+ << (members.size() > 0 ? "rhs" : "/* rhs */") << ") const" << endl;
+ scope_up(out);
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ // Most existing Thrift code does not use isset or optional/required,
+ // so we treat "default" fields as required.
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ out << indent() << "if (!(" << (*m_iter)->get_name() << " == rhs."
+ << (*m_iter)->get_name() << "))" << endl << indent() << " return false;" << endl;
+ } else {
+ out << indent() << "if (__isset." << (*m_iter)->get_name() << " != rhs.__isset."
+ << (*m_iter)->get_name() << ")" << endl << indent() << " return false;" << endl
+ << indent() << "else if (__isset." << (*m_iter)->get_name() << " && !("
+ << (*m_iter)->get_name() << " == rhs." << (*m_iter)->get_name() << "))" << endl
+ << indent() << " return false;" << endl;
+ }
+ }
+ indent(out) << "return true;" << endl;
+ scope_down(out);
+ out << indent() << "bool operator != (const " << tstruct->get_name() << " &rhs) const {"
+ << endl << indent() << " return !(*this == rhs);" << endl << indent() << "}" << endl
+ << endl;
+
+ // Generate the declaration of a less-than operator. This must be
+ // implemented by the application developer if they wish to use it. (They
+ // will get a link error if they try to use it without an implementation.)
+ out << indent() << "bool operator < (const " << tstruct->get_name() << " & ) const;" << endl
+ << endl;
+ }
+ }
+
+ if (read) {
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl << indent()
+ << "uint32_t read(Protocol_* iprot);" << endl;
+ } else {
+ out << indent() << "uint32_t read("
+ << "::apache::thrift::protocol::TProtocol* iprot);" << endl;
+ }
+ }
+ if (write) {
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl << indent()
+ << "uint32_t write(Protocol_* oprot) const;" << endl;
+ } else {
+ out << indent() << "uint32_t write("
+ << "::apache::thrift::protocol::TProtocol* oprot) const;" << endl;
+ }
+ }
+ out << endl;
+
+ if (is_user_struct && !has_custom_ostream(tstruct)) {
+ out << indent() << "virtual ";
+ generate_struct_print_method_decl(out, NULL);
+ out << ";" << endl;
+ }
+
+ // std::exception::what()
+ if (is_exception) {
+ out << indent() << "mutable std::string thriftTExceptionMessageHolder_;" << endl;
+ out << indent();
+ generate_exception_what_method_decl(out, tstruct, false);
+ out << ";" << endl;
+ }
+
+ indent_down();
+ indent(out) << "};" << endl << endl;
+
+ if (swap) {
+ // Generate a namespace-scope swap() function
+ out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name()
+ << " &b);" << endl << endl;
+ }
+
+ if (is_user_struct) {
+ generate_struct_ostream_operator_decl(out, tstruct);
+ }
+}
+
+void t_cpp_generator::generate_struct_definition(ostream& out,
+ ostream& force_cpp_out,
+ t_struct* tstruct,
+ bool setters,
+ bool is_user_struct) {
+ // Get members
+ vector<t_field*>::const_iterator m_iter;
+ const vector<t_field*>& members = tstruct->get_members();
+
+ // Destructor
+ if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) {
+ force_cpp_out << endl << indent() << tstruct->get_name() << "::~" << tstruct->get_name()
+ << "() noexcept {" << endl;
+ indent_up();
+
+ indent_down();
+ force_cpp_out << indent() << "}" << endl << endl;
+ }
+
+ // Create a setter function for each field
+ if (setters) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (is_reference((*m_iter))) {
+ out << endl << indent() << "void " << tstruct->get_name() << "::__set_"
+ << (*m_iter)->get_name() << "(::std::shared_ptr<"
+ << type_name((*m_iter)->get_type(), false, false) << ">";
+ out << " val) {" << endl;
+ } else {
+ out << endl << indent() << "void " << tstruct->get_name() << "::__set_"
+ << (*m_iter)->get_name() << "(" << type_name((*m_iter)->get_type(), false, true);
+ out << " val) {" << endl;
+ }
+ indent_up();
+ out << indent() << "this->" << (*m_iter)->get_name() << " = val;" << endl;
+ indent_down();
+
+ // assume all fields are required except optional fields.
+ // for optional fields change __isset.name to true
+ bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
+ if (is_optional) {
+ out << indent() << indent() << "__isset." << (*m_iter)->get_name() << " = true;" << endl;
+ }
+ out << indent() << "}" << endl;
+ }
+ }
+ if (is_user_struct) {
+ generate_struct_ostream_operator(out, tstruct);
+ }
+ out << endl;
+}
+
+/**
+ * Makes a helper function to gen a struct reader.
+ *
+ * @param out Stream to write to
+ * @param tstruct The struct
+ */
+void t_cpp_generator::generate_struct_reader(ostream& out, t_struct* tstruct, bool pointers) {
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
+ << tstruct->get_name() << "::read(Protocol_* iprot) {" << endl;
+ } else {
+ indent(out) << "uint32_t " << tstruct->get_name()
+ << "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl;
+ }
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Declare stack tmp variables
+ out << endl
+ << indent() << "::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);" << endl
+ << indent() << "uint32_t xfer = 0;" << endl
+ << indent() << "std::string fname;" << endl
+ << indent() << "::apache::thrift::protocol::TType ftype;" << endl
+ << indent() << "int16_t fid;" << endl
+ << endl
+ << indent() << "xfer += iprot->readStructBegin(fname);" << endl
+ << endl
+ << indent() << "using ::apache::thrift::protocol::TProtocolException;" << endl
+ << endl;
+
+ // Required variables aren't in __isset, so we need tmp vars to check them.
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED)
+ indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
+ }
+ out << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true)" << endl;
+ scope_up(out);
+
+ // Read beginning field marker
+ indent(out) << "xfer += iprot->readFieldBegin(fname, ftype, fid);" << endl;
+
+ // Check for field STOP marker
+ out << indent() << "if (ftype == ::apache::thrift::protocol::T_STOP) {" << endl << indent()
+ << " break;" << endl << indent() << "}" << endl;
+
+ if (fields.empty()) {
+ out << indent() << "xfer += iprot->skip(ftype);" << endl;
+ } else {
+ // Switch statement on the field we are reading
+ indent(out) << "switch (fid)" << endl;
+
+ scope_up(out);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ const char* isset_prefix = ((*f_iter)->get_req() != t_field::T_REQUIRED) ? "this->__isset."
+ : "isset_";
+
+#if 0
+ // This code throws an exception if the same field is encountered twice.
+ // We've decided to leave it out for performance reasons.
+ // TODO(dreiss): Generate this code and "if" it out to make it easier
+ // for people recompiling thrift to include it.
+ out <<
+ indent() << "if (" << isset_prefix << (*f_iter)->get_name() << ")" << endl <<
+ indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
+#endif
+
+ if (pointers && !(*f_iter)->get_type()->is_xception()) {
+ generate_deserialize_field(out, *f_iter, "(*(this->", "))");
+ } else {
+ generate_deserialize_field(out, *f_iter, "this->");
+ }
+ out << indent() << isset_prefix << (*f_iter)->get_name() << " = true;" << endl;
+ indent_down();
+ out << indent() << "} else {" << endl << indent() << " xfer += iprot->skip(ftype);" << endl
+ <<
+ // TODO(dreiss): Make this an option when thrift structs
+ // have a common base class.
+ // indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl <<
+ indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ out << indent() << "default:" << endl << indent() << " xfer += iprot->skip(ftype);" << endl
+ << indent() << " break;" << endl;
+
+ scope_down(out);
+ } //!fields.empty()
+ // Read field end marker
+ indent(out) << "xfer += iprot->readFieldEnd();" << endl;
+
+ scope_down(out);
+
+ out << endl << indent() << "xfer += iprot->readStructEnd();" << endl;
+
+ // Throw if any required fields are missing.
+ // We do this after reading the struct end so that
+ // there might possibly be a chance of continuing.
+ out << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED)
+ out << indent() << "if (!isset_" << (*f_iter)->get_name() << ')' << endl << indent()
+ << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
+ }
+
+ indent(out) << "return xfer;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates the write function.
+ *
+ * @param out Stream to write to
+ * @param tstruct The struct
+ */
+void t_cpp_generator::generate_struct_writer(ostream& out, t_struct* tstruct, bool pointers) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
+ << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
+ } else {
+ indent(out) << "uint32_t " << tstruct->get_name()
+ << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+ }
+ indent_up();
+
+ out << indent() << "uint32_t xfer = 0;" << endl;
+
+ indent(out) << "::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);" << endl;
+ indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool check_if_set = (*f_iter)->get_req() == t_field::T_OPTIONAL
+ || (*f_iter)->get_type()->is_xception();
+ if (check_if_set) {
+ out << endl << indent() << "if (this->__isset." << (*f_iter)->get_name() << ") {" << endl;
+ indent_up();
+ } else {
+ out << endl;
+ }
+
+ // Write field header
+ out << indent() << "xfer += oprot->writeFieldBegin("
+ << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", "
+ << (*f_iter)->get_key() << ");" << endl;
+ // Write field contents
+ if (pointers && !(*f_iter)->get_type()->is_xception()) {
+ generate_serialize_field(out, *f_iter, "(*(this->", "))");
+ } else {
+ generate_serialize_field(out, *f_iter, "this->");
+ }
+ // Write field closer
+ indent(out) << "xfer += oprot->writeFieldEnd();" << endl;
+ if (check_if_set) {
+ indent_down();
+ indent(out) << '}';
+ }
+ }
+
+ out << endl;
+
+ // Write the struct map
+ out << indent() << "xfer += oprot->writeFieldStop();" << endl << indent()
+ << "xfer += oprot->writeStructEnd();" << endl << indent()
+ << "return xfer;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Struct writer for result of a function, which can have only one of its
+ * fields set and does a conditional if else look up into the __isset field
+ * of the struct.
+ *
+ * @param out Output stream
+ * @param tstruct The result struct
+ */
+void t_cpp_generator::generate_struct_result_writer(ostream& out,
+ t_struct* tstruct,
+ bool pointers) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t "
+ << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl;
+ } else {
+ indent(out) << "uint32_t " << tstruct->get_name()
+ << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+ }
+ indent_up();
+
+ out << endl << indent() << "uint32_t xfer = 0;" << endl << endl;
+
+ indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl;
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ out << endl << indent() << "if ";
+ } else {
+ out << " else if ";
+ }
+
+ out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl;
+
+ indent_up();
+
+ // Write field header
+ out << indent() << "xfer += oprot->writeFieldBegin("
+ << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", "
+ << (*f_iter)->get_key() << ");" << endl;
+ // Write field contents
+ if (pointers) {
+ generate_serialize_field(out, *f_iter, "(*(this->", "))");
+ } else {
+ generate_serialize_field(out, *f_iter, "this->");
+ }
+ // Write field closer
+ indent(out) << "xfer += oprot->writeFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}";
+ }
+
+ // Write the struct map
+ out << endl << indent() << "xfer += oprot->writeFieldStop();" << endl << indent()
+ << "xfer += oprot->writeStructEnd();" << endl << indent() << "return xfer;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates the swap function.
+ *
+ * @param out Stream to write to
+ * @param tstruct The struct
+ */
+void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) {
+ out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name()
+ << " &b) {" << endl;
+ indent_up();
+
+ // Let argument-dependent name lookup find the correct swap() function to
+ // use based on the argument types. If none is found in the arguments'
+ // namespaces, fall back to ::std::swap().
+ out << indent() << "using ::std::swap;" << endl;
+
+ bool has_nonrequired_fields = false;
+ const vector<t_field*>& fields = tstruct->get_members();
+ for (auto tfield : fields) {
+ if (tfield->get_req() != t_field::T_REQUIRED) {
+ has_nonrequired_fields = true;
+ }
+
+ out << indent() << "swap(a." << tfield->get_name() << ", b." << tfield->get_name() << ");"
+ << endl;
+ }
+
+ if (has_nonrequired_fields) {
+ out << indent() << "swap(a.__isset, b.__isset);" << endl;
+ }
+
+ // handle empty structs
+ if (fields.size() == 0) {
+ out << indent() << "(void) a;" << endl;
+ out << indent() << "(void) b;" << endl;
+ }
+
+ scope_down(out);
+ out << endl;
+}
+
+void t_cpp_generator::generate_struct_ostream_operator_decl(std::ostream& out, t_struct* tstruct) {
+ out << "std::ostream& operator<<(std::ostream& out, const "
+ << tstruct->get_name()
+ << "& obj);" << endl;
+ out << endl;
+}
+
+void t_cpp_generator::generate_struct_ostream_operator(std::ostream& out, t_struct* tstruct) {
+ if (!has_custom_ostream(tstruct)) {
+ // thrift defines this behavior
+ out << "std::ostream& operator<<(std::ostream& out, const "
+ << tstruct->get_name()
+ << "& obj)" << endl;
+ scope_up(out);
+ out << indent() << "obj.printTo(out);" << endl
+ << indent() << "return out;" << endl;
+ scope_down(out);
+ out << endl;
+ }
+}
+
+void t_cpp_generator::generate_struct_print_method_decl(std::ostream& out, t_struct* tstruct) {
+ out << "void ";
+ if (tstruct) {
+ out << tstruct->get_name() << "::";
+ }
+ out << "printTo(std::ostream& out) const";
+}
+
+void t_cpp_generator::generate_exception_what_method_decl(std::ostream& out,
+ t_struct* tstruct,
+ bool external) {
+ out << "const char* ";
+ if (external) {
+ out << tstruct->get_name() << "::";
+ }
+ out << "what() const noexcept";
+}
+
+namespace struct_ostream_operator_generator {
+void generate_required_field_value(std::ostream& out, const t_field* field) {
+ out << " << to_string(" << field->get_name() << ")";
+}
+
+void generate_optional_field_value(std::ostream& out, const t_field* field) {
+ out << "; (__isset." << field->get_name() << " ? (out";
+ generate_required_field_value(out, field);
+ out << ") : (out << \"<null>\"))";
+}
+
+void generate_field_value(std::ostream& out, const t_field* field) {
+ if (field->get_req() == t_field::T_OPTIONAL)
+ generate_optional_field_value(out, field);
+ else
+ generate_required_field_value(out, field);
+}
+
+void generate_field_name(std::ostream& out, const t_field* field) {
+ out << "\"" << field->get_name() << "=\"";
+}
+
+void generate_field(std::ostream& out, const t_field* field) {
+ generate_field_name(out, field);
+ generate_field_value(out, field);
+}
+
+void generate_fields(std::ostream& out,
+ const vector<t_field*>& fields,
+ const std::string& indent) {
+ const vector<t_field*>::const_iterator beg = fields.begin();
+ const vector<t_field*>::const_iterator end = fields.end();
+
+ for (vector<t_field*>::const_iterator it = beg; it != end; ++it) {
+ out << indent << "out << ";
+
+ if (it != beg) {
+ out << "\", \" << ";
+ }
+
+ generate_field(out, *it);
+ out << ";" << endl;
+ }
+}
+}
+
+/**
+ * Generates operator<<
+ */
+void t_cpp_generator::generate_struct_print_method(std::ostream& out, t_struct* tstruct) {
+ out << indent();
+ generate_struct_print_method_decl(out, tstruct);
+ out << " {" << endl;
+
+ indent_up();
+
+ out << indent() << "using ::apache::thrift::to_string;" << endl;
+ out << indent() << "out << \"" << tstruct->get_name() << "(\";" << endl;
+ struct_ostream_operator_generator::generate_fields(out, tstruct->get_members(), indent());
+ out << indent() << "out << \")\";" << endl;
+
+ indent_down();
+ out << "}" << endl << endl;
+}
+
+/**
+ * Generates what() method for exceptions
+ */
+void t_cpp_generator::generate_exception_what_method(std::ostream& out, t_struct* tstruct) {
+ out << indent();
+ generate_exception_what_method_decl(out, tstruct, true);
+ out << " {" << endl;
+
+ indent_up();
+ out << indent() << "try {" << endl;
+
+ indent_up();
+ out << indent() << "std::stringstream ss;" << endl;
+ out << indent() << "ss << \"TException - service has thrown: \" << *this;" << endl;
+ out << indent() << "this->thriftTExceptionMessageHolder_ = ss.str();" << endl;
+ out << indent() << "return this->thriftTExceptionMessageHolder_.c_str();" << endl;
+ indent_down();
+
+ out << indent() << "} catch (const std::exception&) {" << endl;
+
+ indent_up();
+ out << indent() << "return \"TException - service has thrown: " << tstruct->get_name() << "\";"
+ << endl;
+ indent_down();
+
+ out << indent() << "}" << endl;
+
+ indent_down();
+ out << "}" << endl << endl;
+}
+
+/**
+ * Generates a thrift service. In C++, this comprises an entirely separate
+ * header and source file. The header file defines the methods and includes
+ * the data types defined in the main header file, and the implementation
+ * file contains implementations of the basic printer and default interfaces.
+ *
+ * @param tservice The service definition
+ */
+void t_cpp_generator::generate_service(t_service* tservice) {
+ string svcname = tservice->get_name();
+
+ // Make output files
+ string f_header_name = get_out_dir() + svcname + ".h";
+ f_header_.open(f_header_name.c_str());
+
+ // Print header file includes
+ f_header_ << autogen_comment();
+ f_header_ << "#ifndef " << svcname << "_H" << endl << "#define " << svcname << "_H" << endl
+ << endl;
+ if (gen_cob_style_) {
+ f_header_ << "#include <thrift/transport/TBufferTransports.h>" << endl // TMemoryBuffer
+ << "#include <functional>" << endl
+ << "namespace apache { namespace thrift { namespace async {" << endl
+ << "class TAsyncChannel;" << endl << "}}}" << endl;
+ }
+ f_header_ << "#include <thrift/TDispatchProcessor.h>" << endl;
+ if (gen_cob_style_) {
+ f_header_ << "#include <thrift/async/TAsyncDispatchProcessor.h>" << endl;
+ }
+ f_header_ << "#include <thrift/async/TConcurrentClientSyncInfo.h>" << endl;
+ f_header_ << "#include <memory>" << endl;
+ f_header_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ << "_types.h\""
+ << endl;
+
+ t_service* extends_service = tservice->get_extends();
+ if (extends_service != NULL) {
+ f_header_ << "#include \"" << get_include_prefix(*(extends_service->get_program()))
+ << extends_service->get_name() << ".h\"" << endl;
+ }
+
+ f_header_ << endl << ns_open_ << endl << endl;
+
+ f_header_ << "#ifdef _MSC_VER\n"
+ " #pragma warning( push )\n"
+ " #pragma warning (disable : 4250 ) //inheriting methods via dominance \n"
+ "#endif\n\n";
+
+ // Service implementation file includes
+ string f_service_name = get_out_dir() + svcname + ".cpp";
+ f_service_.open(f_service_name.c_str());
+ f_service_ << autogen_comment();
+ f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl;
+ if (gen_cob_style_) {
+ f_service_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl;
+ }
+ if (gen_templates_) {
+ f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\""
+ << endl;
+
+ string f_service_tcc_name = get_out_dir() + svcname + ".tcc";
+ f_service_tcc_.open(f_service_tcc_name.c_str());
+ f_service_tcc_ << autogen_comment();
+ f_service_tcc_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\""
+ << endl;
+
+ f_service_tcc_ << "#ifndef " << svcname << "_TCC" << endl << "#define " << svcname << "_TCC"
+ << endl << endl;
+
+ if (gen_cob_style_) {
+ f_service_tcc_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl;
+ }
+ }
+
+ f_service_ << endl << ns_open_ << endl << endl;
+ f_service_tcc_ << endl << ns_open_ << endl << endl;
+
+ // Generate all the components
+ generate_service_interface(tservice, "");
+ generate_service_interface_factory(tservice, "");
+ generate_service_null(tservice, "");
+ generate_service_helpers(tservice);
+ generate_service_client(tservice, "");
+ generate_service_processor(tservice, "");
+ generate_service_multiface(tservice);
+ generate_service_client(tservice, "Concurrent");
+
+ // Generate skeleton
+ if (!gen_no_skeleton_) {
+ generate_service_skeleton(tservice);
+ }
+
+ // Generate all the cob components
+ if (gen_cob_style_) {
+ generate_service_interface(tservice, "CobCl");
+ generate_service_interface(tservice, "CobSv");
+ generate_service_interface_factory(tservice, "CobSv");
+ generate_service_null(tservice, "CobSv");
+ generate_service_client(tservice, "Cob");
+ generate_service_processor(tservice, "Cob");
+
+ if (!gen_no_skeleton_) {
+ generate_service_async_skeleton(tservice);
+ }
+
+ }
+
+ f_header_ << "#ifdef _MSC_VER\n"
+ " #pragma warning( pop )\n"
+ "#endif\n\n";
+
+ // Close the namespace
+ f_service_ << ns_close_ << endl << endl;
+ f_service_tcc_ << ns_close_ << endl << endl;
+ f_header_ << ns_close_ << endl << endl;
+
+ // TODO(simpkins): Make this a separate option
+ if (gen_templates_) {
+ f_header_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\"" << endl
+ << "#include \"" << get_include_prefix(*get_program()) << program_name_
+ << "_types.tcc\"" << endl << endl;
+ }
+
+ f_header_ << "#endif" << endl;
+ f_service_tcc_ << "#endif" << endl;
+
+ // Close the files
+ f_service_tcc_.close();
+ f_service_.close();
+ f_header_.close();
+}
+
+/**
+ * Generates helper functions for a service. Basically, this generates types
+ * for all the arguments and results to functions.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_cpp_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ string name_orig = ts->get_name();
+
+ // TODO(dreiss): Why is this stuff not in generate_function_helpers?
+ ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_args");
+ generate_struct_declaration(f_header_, ts, false);
+ generate_struct_definition(out, f_service_, ts, false);
+ generate_struct_reader(out, ts);
+ generate_struct_writer(out, ts);
+ ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs");
+ generate_struct_declaration(f_header_, ts, false, true, false, true);
+ generate_struct_definition(out, f_service_, ts, false);
+ generate_struct_writer(out, ts, true);
+ ts->set_name(name_orig);
+
+ generate_function_helpers(tservice, *f_iter);
+ }
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_cpp_generator::generate_service_interface(t_service* tservice, string style) {
+
+ string service_if_name = service_name_ + style + "If";
+ if (style == "CobCl") {
+ // Forward declare the client.
+ string client_name = service_name_ + "CobClient";
+ if (gen_templates_) {
+ client_name += "T";
+ service_if_name += "T";
+ indent(f_header_) << "template <class Protocol_>" << endl;
+ }
+ indent(f_header_) << "class " << client_name << ";" << endl << endl;
+ }
+
+ string extends = "";
+ if (tservice->get_extends() != NULL) {
+ extends = " : virtual public " + type_name(tservice->get_extends()) + style + "If";
+ if (style == "CobCl" && gen_templates_) {
+ // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+ // parent services were also generated with templates enabled.
+ extends += "T<Protocol_>";
+ }
+ }
+
+ if (style == "CobCl" && gen_templates_) {
+ f_header_ << "template <class Protocol_>" << endl;
+ }
+ f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl;
+ indent_up();
+ f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ if ((*f_iter)->has_doc())
+ f_header_ << endl;
+ generate_java_doc(f_header_, *f_iter);
+ f_header_ << indent() << "virtual " << function_signature(*f_iter, style) << " = 0;" << endl;
+ }
+ indent_down();
+ f_header_ << "};" << endl << endl;
+
+ if (style == "CobCl" && gen_templates_) {
+ // generate a backwards-compatible typedef for clients that do not
+ // know about the new template-style code
+ f_header_ << "typedef " << service_if_name << "< ::apache::thrift::protocol::TProtocol> "
+ << service_name_ << style << "If;" << endl << endl;
+ }
+}
+
+/**
+ * Generates a service interface factory.
+ *
+ * @param tservice The service to generate an interface factory for.
+ */
+void t_cpp_generator::generate_service_interface_factory(t_service* tservice, string style) {
+ string service_if_name = service_name_ + style + "If";
+
+ // Figure out the name of the upper-most parent class.
+ // Getting everything to work out properly with inheritance is annoying.
+ // Here's what we're doing for now:
+ //
+ // - All handlers implement getHandler(), but subclasses use covariant return
+ // types to return their specific service interface class type. We have to
+ // use raw pointers because of this; shared_ptr<> can't be used for
+ // covariant return types.
+ //
+ // - Since we're not using shared_ptr<>, we also provide a releaseHandler()
+ // function that must be called to release a pointer to a handler obtained
+ // via getHandler().
+ //
+ // releaseHandler() always accepts a pointer to the upper-most parent class
+ // type. This is necessary since the parent versions of releaseHandler()
+ // may accept any of the parent types, not just the most specific subclass
+ // type. Implementations can use dynamic_cast to cast the pointer to the
+ // subclass type if desired.
+ t_service* base_service = tservice;
+ while (base_service->get_extends() != NULL) {
+ base_service = base_service->get_extends();
+ }
+ string base_if_name = type_name(base_service) + style + "If";
+
+ // Generate the abstract factory class
+ string factory_name = service_if_name + "Factory";
+ string extends;
+ if (tservice->get_extends() != NULL) {
+ extends = " : virtual public " + type_name(tservice->get_extends()) + style + "IfFactory";
+ }
+
+ f_header_ << "class " << factory_name << extends << " {" << endl << " public:" << endl;
+ indent_up();
+ f_header_ << indent() << "typedef " << service_if_name << " Handler;" << endl << endl << indent()
+ << "virtual ~" << factory_name << "() {}" << endl << endl << indent() << "virtual "
+ << service_if_name << "* getHandler("
+ << "const ::apache::thrift::TConnectionInfo& connInfo) = 0;" << endl << indent()
+ << "virtual void releaseHandler(" << base_if_name << "* /* handler */) = 0;" << endl;
+
+ indent_down();
+ f_header_ << "};" << endl << endl;
+
+ // Generate the singleton factory class
+ string singleton_factory_name = service_if_name + "SingletonFactory";
+ f_header_ << "class " << singleton_factory_name << " : virtual public " << factory_name << " {"
+ << endl << " public:" << endl;
+ indent_up();
+ f_header_ << indent() << singleton_factory_name << "(const ::std::shared_ptr<" << service_if_name
+ << ">& iface) : iface_(iface) {}" << endl << indent() << "virtual ~"
+ << singleton_factory_name << "() {}" << endl << endl << indent() << "virtual "
+ << service_if_name << "* getHandler("
+ << "const ::apache::thrift::TConnectionInfo&) {" << endl << indent()
+ << " return iface_.get();" << endl << indent() << "}" << endl << indent()
+ << "virtual void releaseHandler(" << base_if_name << "* /* handler */) {}" << endl;
+
+ f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr<" << service_if_name
+ << "> iface_;" << endl;
+
+ indent_down();
+ f_header_ << "};" << endl << endl;
+}
+
+/**
+ * Generates a null implementation of the service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_cpp_generator::generate_service_null(t_service* tservice, string style) {
+ string extends = "";
+ if (tservice->get_extends() != NULL) {
+ extends = " , virtual public " + type_name(tservice->get_extends()) + style + "Null";
+ }
+ f_header_ << "class " << service_name_ << style << "Null : virtual public " << service_name_
+ << style << "If" << extends << " {" << endl << " public:" << endl;
+ indent_up();
+ f_header_ << indent() << "virtual ~" << service_name_ << style << "Null() {}" << endl;
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_header_ << indent() << function_signature(*f_iter, style, "", false) << " {" << endl;
+ indent_up();
+
+ t_type* returntype = (*f_iter)->get_returntype();
+ t_field returnfield(returntype, "_return");
+
+ if (style == "") {
+ if (returntype->is_void() || is_complex_type(returntype)) {
+ f_header_ << indent() << "return;" << endl;
+ } else {
+ f_header_ << indent() << declare_field(&returnfield, true) << endl << indent()
+ << "return _return;" << endl;
+ }
+ } else if (style == "CobSv") {
+ if (returntype->is_void()) {
+ f_header_ << indent() << "return cob();" << endl;
+ } else {
+ t_field returnfield(returntype, "_return");
+ f_header_ << indent() << declare_field(&returnfield, true) << endl << indent()
+ << "return cob(_return);" << endl;
+ }
+
+ } else {
+ throw "UNKNOWN STYLE";
+ }
+
+ indent_down();
+ f_header_ << indent() << "}" << endl;
+ }
+ indent_down();
+ f_header_ << "};" << endl << endl;
+}
+
+void t_cpp_generator::generate_function_call(ostream& out,
+ t_function* tfunction,
+ string target,
+ string iface,
+ string arg_prefix) {
+ bool first = true;
+ t_type* ret_type = get_true_type(tfunction->get_returntype());
+ out << indent();
+ if (!tfunction->is_oneway() && !ret_type->is_void()) {
+ if (is_complex_type(ret_type)) {
+ first = false;
+ out << iface << "->" << tfunction->get_name() << "(" << target;
+ } else {
+ out << target << " = " << iface << "->" << tfunction->get_name() << "(";
+ }
+ } else {
+ out << iface << "->" << tfunction->get_name() << "(";
+ }
+ const std::vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ", ";
+ }
+ out << arg_prefix << (*f_iter)->get_name();
+ }
+ out << ");" << endl;
+}
+
+void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) {
+ string svcname = tservice->get_name();
+
+ // Service implementation file includes
+ string f_skeleton_name = get_out_dir() + svcname + "_async_server.skeleton.cpp";
+
+ string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp"));
+
+ ofstream_with_content_based_conditional_update f_skeleton;
+ f_skeleton.open(f_skeleton_name.c_str());
+ f_skeleton << "// This autogenerated skeleton file illustrates one way to adapt a synchronous"
+ << endl << "// interface into an asynchronous interface. You should copy it to another"
+ << endl
+ << "// filename to avoid overwriting it and rewrite as asynchronous any functions"
+ << endl << "// that would otherwise introduce unwanted latency." << endl << endl
+ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl
+ << "#include <thrift/protocol/TBinaryProtocol.h>" << endl << endl
+ << "using namespace ::apache::thrift;" << endl
+ << "using namespace ::apache::thrift::protocol;" << endl
+ << "using namespace ::apache::thrift::transport;" << endl
+ << "using namespace ::apache::thrift::async;" << endl << endl;
+
+ // the following code would not compile:
+ // using namespace ;
+ // using namespace ::;
+ if ((!ns.empty()) && (ns.compare(" ::") != 0)) {
+ f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << endl << endl;
+ }
+
+ f_skeleton << "class " << svcname << "AsyncHandler : "
+ << "public " << svcname << "CobSvIf {" << endl << " public:" << endl;
+ indent_up();
+ f_skeleton << indent() << svcname << "AsyncHandler() {" << endl << indent()
+ << " syncHandler_ = std::auto_ptr<" << svcname << "Handler>(new " << svcname
+ << "Handler);" << endl << indent() << " // Your initialization goes here" << endl
+ << indent() << "}" << endl;
+ f_skeleton << indent() << "virtual ~" << service_name_ << "AsyncHandler();" << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_skeleton << endl << indent() << function_signature(*f_iter, "CobSv", "", true) << " {"
+ << endl;
+ indent_up();
+
+ t_type* returntype = (*f_iter)->get_returntype();
+ t_field returnfield(returntype, "_return");
+
+ string target = returntype->is_void() ? "" : "_return";
+ if (!returntype->is_void()) {
+ f_skeleton << indent() << declare_field(&returnfield, true) << endl;
+ }
+ generate_function_call(f_skeleton, *f_iter, target, "syncHandler_", "");
+ f_skeleton << indent() << "return cob(" << target << ");" << endl;
+
+ scope_down(f_skeleton);
+ }
+ f_skeleton << endl << " protected:" << endl << indent() << "std::auto_ptr<" << svcname
+ << "Handler> syncHandler_;" << endl;
+ indent_down();
+ f_skeleton << "};" << endl << endl;
+}
+
+/**
+ * Generates a multiface, which is a single server that just takes a set
+ * of objects implementing the interface and calls them all, returning the
+ * value of the last one to be called.
+ *
+ * @param tservice The service to generate a multiserver for.
+ */
+void t_cpp_generator::generate_service_multiface(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_multiface = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_multiface = ", public " + extends + "Multiface";
+ }
+
+ string list_type = string("std::vector<std::shared_ptr<") + service_name_ + "If> >";
+
+ // Generate the header portion
+ f_header_ << "class " << service_name_ << "Multiface : "
+ << "virtual public " << service_name_ << "If" << extends_multiface << " {" << endl
+ << " public:" << endl;
+ indent_up();
+ f_header_ << indent() << service_name_ << "Multiface(" << list_type
+ << "& ifaces) : ifaces_(ifaces) {" << endl;
+ if (!extends.empty()) {
+ f_header_ << indent()
+ << " std::vector<std::shared_ptr<" + service_name_ + "If> >::iterator iter;"
+ << endl << indent() << " for (iter = ifaces.begin(); iter != ifaces.end(); ++iter) {"
+ << endl << indent() << " " << extends << "Multiface::add(*iter);" << endl
+ << indent() << " }" << endl;
+ }
+ f_header_ << indent() << "}" << endl << indent() << "virtual ~" << service_name_
+ << "Multiface() {}" << endl;
+ indent_down();
+
+ // Protected data members
+ f_header_ << " protected:" << endl;
+ indent_up();
+ f_header_ << indent() << list_type << " ifaces_;" << endl << indent() << service_name_
+ << "Multiface() {}" << endl << indent() << "void add(::std::shared_ptr<"
+ << service_name_ << "If> iface) {" << endl;
+ if (!extends.empty()) {
+ f_header_ << indent() << " " << extends << "Multiface::add(iface);" << endl;
+ }
+ f_header_ << indent() << " ifaces_.push_back(iface);" << endl << indent() << "}" << endl;
+ indent_down();
+
+ f_header_ << indent() << " public:" << endl;
+ indent_up();
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arglist = (*f_iter)->get_arglist();
+ const vector<t_field*>& args = arglist->get_members();
+ vector<t_field*>::const_iterator a_iter;
+
+ string call = string("ifaces_[i]->") + (*f_iter)->get_name() + "(";
+ bool first = true;
+ if (is_complex_type((*f_iter)->get_returntype())) {
+ call += "_return";
+ first = false;
+ }
+ for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
+ if (first) {
+ first = false;
+ } else {
+ call += ", ";
+ }
+ call += (*a_iter)->get_name();
+ }
+ call += ")";
+
+ f_header_ << indent() << function_signature(*f_iter, "") << " {" << endl;
+ indent_up();
+ f_header_ << indent() << "size_t sz = ifaces_.size();" << endl << indent() << "size_t i = 0;"
+ << endl << indent() << "for (; i < (sz - 1); ++i) {" << endl;
+ indent_up();
+ f_header_ << indent() << call << ";" << endl;
+ indent_down();
+ f_header_ << indent() << "}" << endl;
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ if (is_complex_type((*f_iter)->get_returntype())) {
+ f_header_ << indent() << call << ";" << endl << indent() << "return;" << endl;
+ } else {
+ f_header_ << indent() << "return " << call << ";" << endl;
+ }
+ } else {
+ f_header_ << indent() << call << ";" << endl;
+ }
+
+ indent_down();
+ f_header_ << indent() << "}" << endl << endl;
+ }
+
+ indent_down();
+ f_header_ << indent() << "};" << endl << endl;
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_cpp_generator::generate_service_client(t_service* tservice, string style) {
+ string ifstyle;
+ if (style == "Cob") {
+ ifstyle = "CobCl";
+ }
+
+ std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+ string template_header, template_suffix, short_suffix, protocol_type, _this;
+ string const prot_factory_type = "::apache::thrift::protocol::TProtocolFactory";
+ if (gen_templates_) {
+ template_header = "template <class Protocol_>\n";
+ short_suffix = "T";
+ template_suffix = "T<Protocol_>";
+ protocol_type = "Protocol_";
+ _this = "this->";
+ } else {
+ protocol_type = "::apache::thrift::protocol::TProtocol";
+ }
+ string prot_ptr = "std::shared_ptr< " + protocol_type + ">";
+ string client_suffix = "Client" + template_suffix;
+ string if_suffix = "If";
+ if (style == "Cob") {
+ if_suffix += template_suffix;
+ }
+
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+ // parent services were also generated with templates enabled.
+ extends = type_name(tservice->get_extends());
+ extends_client = ", public " + extends + style + client_suffix;
+ }
+
+ // Generate the header portion
+ if (style == "Concurrent") {
+ f_header_ << "// The \'concurrent\' client is a thread safe client that correctly handles\n"
+ "// out of order responses. It is slower than the regular client, so should\n"
+ "// only be used when you need to share a connection among multiple threads\n";
+ }
+ f_header_ << template_header << "class " << service_name_ << style << "Client" << short_suffix
+ << " : "
+ << "virtual public " << service_name_ << ifstyle << if_suffix << extends_client << " {"
+ << endl << " public:" << endl;
+
+ indent_up();
+ if (style != "Cob") {
+ f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr
+ << " prot";
+ if (style == "Concurrent") {
+ f_header_ << ", std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync";
+ }
+ f_header_ << ") ";
+
+ if (extends.empty()) {
+ if (style == "Concurrent") {
+ f_header_ << ": sync_(sync)" << endl;
+ }
+ f_header_ << "{" << endl;
+ f_header_ << indent() << " setProtocol" << short_suffix << "(prot);" << endl << indent()
+ << "}" << endl;
+ } else {
+ f_header_ << ":" << endl;
+ f_header_ << indent() << " " << extends << style << client_suffix << "(prot, prot";
+ if (style == "Concurrent") {
+ f_header_ << ", sync";
+ }
+ f_header_ << ") {}" << endl;
+ }
+
+ f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr
+ << " iprot, " << prot_ptr << " oprot";
+ if (style == "Concurrent") {
+ f_header_ << ", std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync";
+ }
+ f_header_ << ") ";
+
+ if (extends.empty()) {
+ if (style == "Concurrent") {
+ f_header_ << ": sync_(sync)" << endl;
+ }
+ f_header_ << "{" << endl;
+ f_header_ << indent() << " setProtocol" << short_suffix << "(iprot,oprot);" << endl
+ << indent() << "}" << endl;
+ } else {
+ f_header_ << ":" << indent() << " " << extends << style << client_suffix
+ << "(iprot, oprot";
+ if (style == "Concurrent") {
+ f_header_ << ", sync";
+ }
+ f_header_ << ") {}" << endl;
+ }
+
+ // create the setProtocol methods
+ if (extends.empty()) {
+ f_header_ << " private:" << endl;
+ // 1: one parameter
+ f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " prot) {"
+ << endl;
+ f_header_ << indent() << "setProtocol" << short_suffix << "(prot,prot);" << endl;
+ f_header_ << indent() << "}" << endl;
+ // 2: two parameter
+ f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " iprot, "
+ << prot_ptr << " oprot) {" << endl;
+
+ f_header_ << indent() << " piprot_=iprot;" << endl << indent() << " poprot_=oprot;" << endl
+ << indent() << " iprot_ = iprot.get();" << endl << indent()
+ << " oprot_ = oprot.get();" << endl;
+
+ f_header_ << indent() << "}" << endl;
+ f_header_ << " public:" << endl;
+ }
+
+ // Generate getters for the protocols.
+ // Note that these are not currently templated for simplicity.
+ // TODO(simpkins): should they be templated?
+ f_header_ << indent()
+ << "std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {"
+ << endl << indent() << " return " << _this << "piprot_;" << endl << indent() << "}"
+ << endl;
+
+ f_header_ << indent()
+ << "std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {"
+ << endl << indent() << " return " << _this << "poprot_;" << endl << indent() << "}"
+ << endl;
+
+ } else /* if (style == "Cob") */ {
+ f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "("
+ << "std::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, "
+ << "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" << endl;
+ if (extends.empty()) {
+ f_header_ << indent() << " channel_(channel)," << endl << indent()
+ << " itrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl
+ << indent() << " otrans_(new ::apache::thrift::transport::TMemoryBuffer()),"
+ << endl;
+ if (gen_templates_) {
+ // TProtocolFactory classes return generic TProtocol pointers.
+ // We have to dynamic cast to the Protocol_ type we are expecting.
+ f_header_ << indent() << " piprot_(::std::dynamic_pointer_cast<Protocol_>("
+ << "protocolFactory->getProtocol(itrans_)))," << endl << indent()
+ << " poprot_(::std::dynamic_pointer_cast<Protocol_>("
+ << "protocolFactory->getProtocol(otrans_))) {" << endl;
+ // Throw a TException if either dynamic cast failed.
+ f_header_ << indent() << " if (!piprot_ || !poprot_) {" << endl << indent()
+ << " throw ::apache::thrift::TException(\""
+ << "TProtocolFactory returned unexpected protocol type in " << service_name_
+ << style << "Client" << short_suffix << " constructor\");" << endl << indent()
+ << " }" << endl;
+ } else {
+ f_header_ << indent() << " piprot_(protocolFactory->getProtocol(itrans_))," << endl
+ << indent() << " poprot_(protocolFactory->getProtocol(otrans_)) {" << endl;
+ }
+ f_header_ << indent() << " iprot_ = piprot_.get();" << endl << indent()
+ << " oprot_ = poprot_.get();" << endl << indent() << "}" << endl;
+ } else {
+ f_header_ << indent() << " " << extends << style << client_suffix
+ << "(channel, protocolFactory) {}" << endl;
+ }
+ }
+
+ if (style == "Cob") {
+ f_header_ << indent()
+ << "::std::shared_ptr< ::apache::thrift::async::TAsyncChannel> getChannel() {" << endl
+ << indent() << " return " << _this << "channel_;" << endl << indent() << "}" << endl;
+ if (!gen_no_client_completion_) {
+ f_header_ << indent() << "virtual void completed__(bool /* success */) {}" << endl;
+ }
+ }
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_header_) << function_signature(*f_iter, ifstyle) << ";" << endl;
+ // TODO(dreiss): Use private inheritance to avoid generating thise in cob-style.
+ if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
+ // concurrent clients need to move the seqid from the send function to the
+ // recv function. Oneway methods don't have a recv function, so we don't need to
+ // move the seqid for them. Attempting to do so would result in a seqid leak.
+ t_function send_function(g_type_i32, /*returning seqid*/
+ string("send_") + (*f_iter)->get_name(),
+ (*f_iter)->get_arglist());
+ indent(f_header_) << function_signature(&send_function, "") << ";" << endl;
+ } else {
+ t_function send_function(g_type_void,
+ string("send_") + (*f_iter)->get_name(),
+ (*f_iter)->get_arglist());
+ indent(f_header_) << function_signature(&send_function, "") << ";" << endl;
+ }
+ if (!(*f_iter)->is_oneway()) {
+ if (style == "Concurrent") {
+ t_field seqIdArg(g_type_i32, "seqid");
+ t_struct seqIdArgStruct(program_);
+ seqIdArgStruct.append(&seqIdArg);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &seqIdArgStruct);
+ indent(f_header_) << function_signature(&recv_function, "") << ";" << endl;
+ } else {
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ indent(f_header_) << function_signature(&recv_function, "") << ";" << endl;
+ }
+ }
+ }
+ indent_down();
+
+ if (extends.empty()) {
+ f_header_ << " protected:" << endl;
+ indent_up();
+
+ if (style == "Cob") {
+ f_header_ << indent()
+ << "::std::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel_;" << endl
+ << indent()
+ << "::std::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> itrans_;" << endl
+ << indent()
+ << "::std::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> otrans_;"
+ << endl;
+ }
+ f_header_ <<
+ indent() << prot_ptr << " piprot_;" << endl <<
+ indent() << prot_ptr << " poprot_;" << endl <<
+ indent() << protocol_type << "* iprot_;" << endl <<
+ indent() << protocol_type << "* oprot_;" << endl;
+
+ if (style == "Concurrent") {
+ f_header_ <<
+ indent() << "std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync_;"<<endl;
+ }
+ indent_down();
+ }
+
+ f_header_ << "};" << endl << endl;
+
+ if (gen_templates_) {
+ // Output a backwards compatibility typedef using
+ // TProtocol as the template parameter.
+ f_header_ << "typedef " << service_name_ << style
+ << "ClientT< ::apache::thrift::protocol::TProtocol> " << service_name_ << style
+ << "Client;" << endl << endl;
+ }
+
+ string scope = service_name_ + style + client_suffix + "::";
+
+ // Generate client method implementations
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string seqIdCapture;
+ string seqIdUse;
+ string seqIdCommaUse;
+ if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
+ seqIdCapture = "int32_t seqid = ";
+ seqIdUse = "seqid";
+ seqIdCommaUse = ", seqid";
+ }
+
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ if (gen_templates_) {
+ indent(out) << template_header;
+ }
+ indent(out) << function_signature(*f_iter, ifstyle, scope) << endl;
+ scope_up(out);
+ indent(out) << seqIdCapture << "send_" << funname << "(";
+
+ // Get the struct of function call params
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ // Declare the function arguments
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ", ";
+ }
+ out << (*fld_iter)->get_name();
+ }
+ out << ");" << endl;
+
+ if (style != "Cob") {
+ if (!(*f_iter)->is_oneway()) {
+ out << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ if (is_complex_type((*f_iter)->get_returntype())) {
+ out << "recv_" << funname << "(_return" << seqIdCommaUse << ");" << endl;
+ } else {
+ out << "return recv_" << funname << "(" << seqIdUse << ");" << endl;
+ }
+ } else {
+ out << "recv_" << funname << "(" << seqIdUse << ");" << endl;
+ }
+ }
+ } else {
+ if (!(*f_iter)->is_oneway()) {
+ out << indent() << _this << "channel_->sendAndRecvMessage("
+ << "::std::bind(cob, this), " << _this << "otrans_.get(), " << _this << "itrans_.get());"
+ << endl;
+ } else {
+ out << indent() << _this << "channel_->sendMessage("
+ << "::std::bind(cob, this), " << _this << "otrans_.get());" << endl;
+ }
+ }
+ scope_down(out);
+ out << endl;
+
+ // if (style != "Cob") // TODO(dreiss): Libify the client and don't generate this for cob-style
+ if (true) {
+ t_type* send_func_return_type = g_type_void;
+ if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
+ send_func_return_type = g_type_i32;
+ }
+ // Function for sending
+ t_function send_function(send_func_return_type,
+ string("send_") + (*f_iter)->get_name(),
+ (*f_iter)->get_arglist());
+
+ // Open the send function
+ if (gen_templates_) {
+ indent(out) << template_header;
+ }
+ indent(out) << function_signature(&send_function, "", scope) << endl;
+ scope_up(out);
+
+ // Function arguments and results
+ string argsname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs";
+ string resultname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_presult";
+
+ string cseqidVal = "0";
+ if (style == "Concurrent") {
+ if (!(*f_iter)->is_oneway()) {
+ cseqidVal = "this->sync_->generateSeqId()";
+ }
+ }
+ // Serialize the request
+ out <<
+ indent() << "int32_t cseqid = " << cseqidVal << ";" << endl;
+ if(style == "Concurrent") {
+ out <<
+ indent() << "::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());" << endl;
+ }
+ if (style == "Cob") {
+ out <<
+ indent() << _this << "otrans_->resetBuffer();" << endl;
+ }
+ out <<
+ indent() << _this << "oprot_->writeMessageBegin(\"" <<
+ (*f_iter)->get_name() <<
+ "\", ::apache::thrift::protocol::" << ((*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL") <<
+ ", cseqid);" << endl << endl <<
+ indent() << argsname << " args;" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ out << indent() << "args." << (*fld_iter)->get_name() << " = &" << (*fld_iter)->get_name()
+ << ";" << endl;
+ }
+
+ out << indent() << "args.write(" << _this << "oprot_);" << endl << endl << indent() << _this
+ << "oprot_->writeMessageEnd();" << endl << indent() << _this
+ << "oprot_->getTransport()->writeEnd();" << endl << indent() << _this
+ << "oprot_->getTransport()->flush();" << endl;
+
+ if (style == "Concurrent") {
+ out << endl << indent() << "sentry.commit();" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ out << indent() << "return cseqid;" << endl;
+ }
+ }
+ scope_down(out);
+ out << endl;
+
+ // Generate recv function only if not an oneway function
+ if (!(*f_iter)->is_oneway()) {
+ t_struct noargs(program_);
+
+ t_field seqIdArg(g_type_i32, "seqid");
+ t_struct seqIdArgStruct(program_);
+ seqIdArgStruct.append(&seqIdArg);
+
+ t_struct* recv_function_args = &noargs;
+ if (style == "Concurrent") {
+ recv_function_args = &seqIdArgStruct;
+ }
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ recv_function_args);
+ // Open the recv function
+ if (gen_templates_) {
+ indent(out) << template_header;
+ }
+ indent(out) << function_signature(&recv_function, "", scope) << endl;
+ scope_up(out);
+
+ out << endl <<
+ indent() << "int32_t rseqid = 0;" << endl <<
+ indent() << "std::string fname;" << endl <<
+ indent() << "::apache::thrift::protocol::TMessageType mtype;" << endl;
+ if(style == "Concurrent") {
+ out <<
+ endl <<
+ indent() << "// the read mutex gets dropped and reacquired as part of waitForWork()" << endl <<
+ indent() << "// The destructor of this sentry wakes up other clients" << endl <<
+ indent() << "::apache::thrift::async::TConcurrentRecvSentry sentry(this->sync_.get(), seqid);" << endl;
+ }
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << "bool completed = false;" << endl << endl << indent() << "try {";
+ indent_up();
+ }
+ out << endl;
+ if (style == "Concurrent") {
+ out <<
+ indent() << "while(true) {" << endl <<
+ indent() << " if(!this->sync_->getPending(fname, mtype, rseqid)) {" << endl;
+ indent_up();
+ indent_up();
+ }
+ out <<
+ indent() << _this << "iprot_->readMessageBegin(fname, mtype, rseqid);" << endl;
+ if (style == "Concurrent") {
+ scope_down(out);
+ out << indent() << "if(seqid == rseqid) {" << endl;
+ indent_up();
+ }
+ out <<
+ indent() << "if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {" << endl <<
+ indent() << " ::apache::thrift::TApplicationException x;" << endl <<
+ indent() << " x.read(" << _this << "iprot_);" << endl <<
+ indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl;
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << indent() << " sentry.commit();" << endl;
+ }
+ out <<
+ indent() << " throw x;" << endl <<
+ indent() << "}" << endl <<
+ indent() << "if (mtype != ::apache::thrift::protocol::T_REPLY) {" << endl <<
+ indent() << " " << _this << "iprot_->skip(" << "::apache::thrift::protocol::T_STRUCT);" << endl <<
+ indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl;
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << " completed = true;" << endl << indent() << " completed__(false);"
+ << endl;
+ }
+ out <<
+ indent() << "}" << endl <<
+ indent() << "if (fname.compare(\"" << (*f_iter)->get_name() << "\") != 0) {" << endl <<
+ indent() << " " << _this << "iprot_->skip(" << "::apache::thrift::protocol::T_STRUCT);" << endl <<
+ indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl;
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << " completed = true;" << endl << indent() << " completed__(false);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << endl <<
+ indent() << " // in a bad state, don't commit" << endl <<
+ indent() << " using ::apache::thrift::protocol::TProtocolException;" << endl <<
+ indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl;
+ }
+ out << indent() << "}" << endl;
+
+ if (!(*f_iter)->get_returntype()->is_void()
+ && !is_complex_type((*f_iter)->get_returntype())) {
+ t_field returnfield((*f_iter)->get_returntype(), "_return");
+ out << indent() << declare_field(&returnfield) << endl;
+ }
+
+ out << indent() << resultname << " result;" << endl;
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << indent() << "result.success = &_return;" << endl;
+ }
+
+ out << indent() << "result.read(" << _this << "iprot_);" << endl << indent() << _this
+ << "iprot_->readMessageEnd();" << endl << indent() << _this
+ << "iprot_->getTransport()->readEnd();" << endl << endl;
+
+ // Careful, only look for _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ if (is_complex_type((*f_iter)->get_returntype())) {
+ out <<
+ indent() << "if (result.__isset.success) {" << endl;
+ out <<
+ indent() << " // _return pointer has now been filled" << endl;
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << indent() << " sentry.commit();" << endl;
+ }
+ out <<
+ indent() << " return;" << endl <<
+ indent() << "}" << endl;
+ } else {
+ out << indent() << "if (result.__isset.success) {" << endl;
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << indent() << " sentry.commit();" << endl;
+ }
+ out << indent() << " return _return;" << endl << indent() << "}" << endl;
+ }
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << indent() << "if (result.__isset." << (*x_iter)->get_name() << ") {" << endl;
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << " completed = true;" << endl << indent() << " completed__(true);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << indent() << " sentry.commit();" << endl;
+ }
+ out << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl << indent()
+ << "}" << endl;
+ }
+
+ // We only get here if we are a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << "completed = true;" << endl << indent() << "completed__(true);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << indent() << "sentry.commit();" << endl;
+ }
+ indent(out) << "return;" << endl;
+ } else {
+ if (style == "Cob" && !gen_no_client_completion_) {
+ out << indent() << "completed = true;" << endl << indent() << "completed__(true);"
+ << endl;
+ }
+ if (style == "Concurrent") {
+ out << indent() << "// in a bad state, don't commit" << endl;
+ }
+ out << indent() << "throw "
+ "::apache::thrift::TApplicationException(::apache::thrift::"
+ "TApplicationException::MISSING_RESULT, \"" << (*f_iter)->get_name()
+ << " failed: unknown result\");" << endl;
+ }
+ if (style == "Concurrent") {
+ indent_down();
+ indent_down();
+ out <<
+ indent() << " }" << endl <<
+ indent() << " // seqid != rseqid" << endl <<
+ indent() << " this->sync_->updatePending(fname, mtype, rseqid);" << endl <<
+ endl <<
+ indent() << " // this will temporarily unlock the readMutex, and let other clients get work done" << endl <<
+ indent() << " this->sync_->waitForWork(seqid);" << endl <<
+ indent() << "} // end while(true)" << endl;
+ }
+ if (style == "Cob" && !gen_no_client_completion_) {
+ indent_down();
+ out << indent() << "} catch (...) {" << endl << indent() << " if (!completed) {" << endl
+ << indent() << " completed__(false);" << endl << indent() << " }" << endl
+ << indent() << " throw;" << endl << indent() << "}" << endl;
+ }
+ // Close function
+ scope_down(out);
+ out << endl;
+ }
+ }
+ }
+}
+
+class ProcessorGenerator {
+public:
+ ProcessorGenerator(t_cpp_generator* generator, t_service* service, const string& style);
+
+ void run() {
+ generate_class_definition();
+
+ // Generate the dispatchCall() function
+ generate_dispatch_call(false);
+ if (generator_->gen_templates_) {
+ generate_dispatch_call(true);
+ }
+
+ // Generate all of the process subfunctions
+ generate_process_functions();
+
+ generate_factory();
+ }
+
+ void generate_class_definition();
+ void generate_dispatch_call(bool template_protocol);
+ void generate_process_functions();
+ void generate_factory();
+
+protected:
+ std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false) {
+ return generator_->type_name(ttype, in_typedef, arg);
+ }
+
+ std::string indent() { return generator_->indent(); }
+ std::ostream& indent(std::ostream& os) { return generator_->indent(os); }
+
+ void indent_up() { generator_->indent_up(); }
+ void indent_down() { generator_->indent_down(); }
+
+ t_cpp_generator* generator_;
+ t_service* service_;
+ std::ostream& f_header_;
+ std::ostream& f_out_;
+ string service_name_;
+ string style_;
+ string pstyle_;
+ string class_name_;
+ string if_name_;
+ string factory_class_name_;
+ string finish_cob_;
+ string finish_cob_decl_;
+ string ret_type_;
+ string call_context_;
+ string cob_arg_;
+ string call_context_arg_;
+ string call_context_decl_;
+ string template_header_;
+ string template_suffix_;
+ string typename_str_;
+ string class_suffix_;
+ string extends_;
+};
+
+ProcessorGenerator::ProcessorGenerator(t_cpp_generator* generator,
+ t_service* service,
+ const string& style)
+ : generator_(generator),
+ service_(service),
+ f_header_(generator->f_header_),
+ f_out_(generator->gen_templates_ ? generator->f_service_tcc_ : generator->f_service_),
+ service_name_(generator->service_name_),
+ style_(style) {
+ if (style_ == "Cob") {
+ pstyle_ = "Async";
+ class_name_ = service_name_ + pstyle_ + "Processor";
+ if_name_ = service_name_ + "CobSvIf";
+
+ finish_cob_ = "::std::function<void(bool ok)> cob, ";
+ finish_cob_decl_ = "::std::function<void(bool ok)>, ";
+ cob_arg_ = "cob, ";
+ ret_type_ = "void ";
+ } else {
+ class_name_ = service_name_ + "Processor";
+ if_name_ = service_name_ + "If";
+
+ ret_type_ = "bool ";
+ // TODO(edhall) callContext should eventually be added to TAsyncProcessor
+ call_context_ = ", void* callContext";
+ call_context_arg_ = ", callContext";
+ call_context_decl_ = ", void*";
+ }
+
+ factory_class_name_ = class_name_ + "Factory";
+
+ if (generator->gen_templates_) {
+ template_header_ = "template <class Protocol_>\n";
+ template_suffix_ = "<Protocol_>";
+ typename_str_ = "typename ";
+ class_name_ += "T";
+ factory_class_name_ += "T";
+ }
+
+ if (service_->get_extends() != NULL) {
+ extends_ = type_name(service_->get_extends()) + pstyle_ + "Processor";
+ if (generator_->gen_templates_) {
+ // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+ // parent services were also generated with templates enabled.
+ extends_ += "T<Protocol_>";
+ }
+ }
+}
+
+void ProcessorGenerator::generate_class_definition() {
+ // Generate the dispatch methods
+ vector<t_function*> functions = service_->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string parent_class;
+ if (service_->get_extends() != NULL) {
+ parent_class = extends_;
+ } else {
+ if (style_ == "Cob") {
+ parent_class = "::apache::thrift::async::TAsyncDispatchProcessor";
+ } else {
+ parent_class = "::apache::thrift::TDispatchProcessor";
+ }
+
+ if (generator_->gen_templates_) {
+ parent_class += "T<Protocol_>";
+ }
+ }
+
+ // Generate the header portion
+ f_header_ << template_header_ << "class " << class_name_ << " : public " << parent_class << " {"
+ << endl;
+
+ // Protected data members
+ f_header_ << " protected:" << endl;
+ indent_up();
+ f_header_ << indent() << "::std::shared_ptr<" << if_name_ << "> iface_;" << endl;
+ f_header_ << indent() << "virtual " << ret_type_ << "dispatchCall(" << finish_cob_
+ << "::apache::thrift::protocol::TProtocol* iprot, "
+ << "::apache::thrift::protocol::TProtocol* oprot, "
+ << "const std::string& fname, int32_t seqid" << call_context_ << ");" << endl;
+ if (generator_->gen_templates_) {
+ f_header_ << indent() << "virtual " << ret_type_ << "dispatchCallTemplated(" << finish_cob_
+ << "Protocol_* iprot, Protocol_* oprot, "
+ << "const std::string& fname, int32_t seqid" << call_context_ << ");" << endl;
+ }
+ indent_down();
+
+ // Process function declarations
+ f_header_ << " private:" << endl;
+ indent_up();
+
+ // Declare processMap_
+ f_header_ << indent() << "typedef void (" << class_name_ << "::*"
+ << "ProcessFunction)(" << finish_cob_decl_ << "int32_t, "
+ << "::apache::thrift::protocol::TProtocol*, "
+ << "::apache::thrift::protocol::TProtocol*" << call_context_decl_ << ");" << endl;
+ if (generator_->gen_templates_) {
+ f_header_ << indent() << "typedef void (" << class_name_ << "::*"
+ << "SpecializedProcessFunction)(" << finish_cob_decl_ << "int32_t, "
+ << "Protocol_*, Protocol_*" << call_context_decl_ << ");" << endl << indent()
+ << "struct ProcessFunctions {" << endl << indent() << " ProcessFunction generic;"
+ << endl << indent() << " SpecializedProcessFunction specialized;" << endl << indent()
+ << " ProcessFunctions(ProcessFunction g, "
+ << "SpecializedProcessFunction s) :" << endl << indent() << " generic(g)," << endl
+ << indent() << " specialized(s) {}" << endl << indent()
+ << " ProcessFunctions() : generic(NULL), specialized(NULL) "
+ << "{}" << endl << indent() << "};" << endl << indent()
+ << "typedef std::map<std::string, ProcessFunctions> "
+ << "ProcessMap;" << endl;
+ } else {
+ f_header_ << indent() << "typedef std::map<std::string, ProcessFunction> "
+ << "ProcessMap;" << endl;
+ }
+ f_header_ << indent() << "ProcessMap processMap_;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_header_) << "void process_" << (*f_iter)->get_name() << "(" << finish_cob_
+ << "int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, "
+ "::apache::thrift::protocol::TProtocol* oprot" << call_context_ << ");"
+ << endl;
+ if (generator_->gen_templates_) {
+ indent(f_header_) << "void process_" << (*f_iter)->get_name() << "(" << finish_cob_
+ << "int32_t seqid, Protocol_* iprot, Protocol_* oprot" << call_context_
+ << ");" << endl;
+ }
+ if (style_ == "Cob") {
+ // XXX Factor this out, even if it is a pain.
+ string ret_arg = ((*f_iter)->get_returntype()->is_void()
+ ? ""
+ : ", const " + type_name((*f_iter)->get_returntype()) + "& _return");
+ f_header_ << indent() << "void return_" << (*f_iter)->get_name()
+ << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << "::apache::thrift::protocol::TProtocol* oprot, "
+ << "void* ctx" << ret_arg << ");" << endl;
+ if (generator_->gen_templates_) {
+ f_header_ << indent() << "void return_" << (*f_iter)->get_name()
+ << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << "Protocol_* oprot, void* ctx" << ret_arg << ");" << endl;
+ }
+ // XXX Don't declare throw if it doesn't exist
+ f_header_ << indent() << "void throw_" << (*f_iter)->get_name()
+ << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << "::apache::thrift::protocol::TProtocol* oprot, void* ctx, "
+ << "::apache::thrift::TDelayedException* _throw);" << endl;
+ if (generator_->gen_templates_) {
+ f_header_ << indent() << "void throw_" << (*f_iter)->get_name()
+ << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << "Protocol_* oprot, void* ctx, "
+ << "::apache::thrift::TDelayedException* _throw);" << endl;
+ }
+ }
+ }
+
+ f_header_ << " public:" << endl << indent() << class_name_ << "(::std::shared_ptr<" << if_name_
+ << "> iface) :" << endl;
+ if (!extends_.empty()) {
+ f_header_ << indent() << " " << extends_ << "(iface)," << endl;
+ }
+ f_header_ << indent() << " iface_(iface) {" << endl;
+ indent_up();
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_header_ << indent() << "processMap_[\"" << (*f_iter)->get_name() << "\"] = ";
+ if (generator_->gen_templates_) {
+ f_header_ << "ProcessFunctions(" << endl;
+ if (generator_->gen_templates_only_) {
+ indent(f_header_) << " NULL," << endl;
+ } else {
+ indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << ","
+ << endl;
+ }
+ indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << ")";
+ } else {
+ f_header_ << "&" << class_name_ << "::process_" << (*f_iter)->get_name();
+ }
+ f_header_ << ";" << endl;
+ }
+
+ indent_down();
+ f_header_ << indent() << "}" << endl << endl << indent() << "virtual ~" << class_name_ << "() {}"
+ << endl;
+ indent_down();
+ f_header_ << "};" << endl << endl;
+
+ if (generator_->gen_templates_) {
+ // Generate a backwards compatible typedef, for callers who don't know
+ // about the new template-style code.
+ //
+ // We can't use TProtocol as the template parameter, since ProcessorT
+ // provides overloaded versions of most methods, one of which accepts
+ // TProtocol pointers, and one which accepts Protocol_ pointers. This
+ // results in a compile error if instantiated with Protocol_ == TProtocol.
+ // Therefore, we define TDummyProtocol solely so we can use it as the
+ // template parameter here.
+ f_header_ << "typedef " << class_name_ << "< ::apache::thrift::protocol::TDummyProtocol > "
+ << service_name_ << pstyle_ << "Processor;" << endl << endl;
+ }
+}
+
+void ProcessorGenerator::generate_dispatch_call(bool template_protocol) {
+ string protocol = "::apache::thrift::protocol::TProtocol";
+ string function_suffix;
+ if (template_protocol) {
+ protocol = "Protocol_";
+ // We call the generic version dispatchCall(), and the specialized
+ // version dispatchCallTemplated(). We can't call them both
+ // dispatchCall(), since this will cause the compiler to issue a warning if
+ // a service that doesn't use templates inherits from a service that does
+ // use templates: the compiler complains that the subclass only implements
+ // the generic version of dispatchCall(), and hides the templated version.
+ // Using different names for the two functions prevents this.
+ function_suffix = "Templated";
+ }
+
+ f_out_ << template_header_ << ret_type_ << class_name_ << template_suffix_ << "::dispatchCall"
+ << function_suffix << "(" << finish_cob_ << protocol << "* iprot, " << protocol
+ << "* oprot, "
+ << "const std::string& fname, int32_t seqid" << call_context_ << ") {" << endl;
+ indent_up();
+
+ // HOT: member function pointer map
+ f_out_ << indent() << typename_str_ << "ProcessMap::iterator pfn;" << endl << indent()
+ << "pfn = processMap_.find(fname);" << endl << indent()
+ << "if (pfn == processMap_.end()) {" << endl;
+ if (extends_.empty()) {
+ f_out_ << indent() << " iprot->skip(::apache::thrift::protocol::T_STRUCT);" << endl << indent()
+ << " iprot->readMessageEnd();" << endl << indent()
+ << " iprot->getTransport()->readEnd();" << endl << indent()
+ << " ::apache::thrift::TApplicationException "
+ "x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, \"Invalid method name: "
+ "'\"+fname+\"'\");" << endl << indent()
+ << " oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid);"
+ << endl << indent() << " x.write(oprot);" << endl << indent()
+ << " oprot->writeMessageEnd();" << endl << indent()
+ << " oprot->getTransport()->writeEnd();" << endl << indent()
+ << " oprot->getTransport()->flush();" << endl << indent()
+ << (style_ == "Cob" ? " return cob(true);" : " return true;") << endl;
+ } else {
+ f_out_ << indent() << " return " << extends_ << "::dispatchCall("
+ << (style_ == "Cob" ? "cob, " : "") << "iprot, oprot, fname, seqid" << call_context_arg_
+ << ");" << endl;
+ }
+ f_out_ << indent() << "}" << endl;
+ if (template_protocol) {
+ f_out_ << indent() << "(this->*(pfn->second.specialized))";
+ } else {
+ if (generator_->gen_templates_only_) {
+ // TODO: This is a null pointer, so nothing good will come from calling
+ // it. Throw an exception instead.
+ f_out_ << indent() << "(this->*(pfn->second.generic))";
+ } else if (generator_->gen_templates_) {
+ f_out_ << indent() << "(this->*(pfn->second.generic))";
+ } else {
+ f_out_ << indent() << "(this->*(pfn->second))";
+ }
+ }
+ f_out_ << "(" << cob_arg_ << "seqid, iprot, oprot" << call_context_arg_ << ");" << endl;
+
+ // TODO(dreiss): return pfn ret?
+ if (style_ == "Cob") {
+ f_out_ << indent() << "return;" << endl;
+ } else {
+ f_out_ << indent() << "return true;" << endl;
+ }
+
+ indent_down();
+ f_out_ << "}" << endl << endl;
+}
+
+void ProcessorGenerator::generate_process_functions() {
+ vector<t_function*> functions = service_->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ if (generator_->gen_templates_) {
+ generator_->generate_process_function(service_, *f_iter, style_, false);
+ generator_->generate_process_function(service_, *f_iter, style_, true);
+ } else {
+ generator_->generate_process_function(service_, *f_iter, style_, false);
+ }
+ }
+}
+
+void ProcessorGenerator::generate_factory() {
+ string if_factory_name = if_name_ + "Factory";
+
+ // Generate the factory class definition
+ f_header_ << template_header_ << "class " << factory_class_name_ << " : public ::apache::thrift::"
+ << (style_ == "Cob" ? "async::TAsyncProcessorFactory" : "TProcessorFactory") << " {"
+ << endl << " public:" << endl;
+ indent_up();
+
+ f_header_ << indent() << factory_class_name_ << "(const ::std::shared_ptr< " << if_factory_name
+ << " >& handlerFactory) :" << endl << indent()
+ << " handlerFactory_(handlerFactory) {}" << endl << endl << indent()
+ << "::std::shared_ptr< ::apache::thrift::"
+ << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
+ << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);" << endl;
+
+ f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr< "
+ << if_factory_name << " > handlerFactory_;" << endl;
+
+ indent_down();
+ f_header_ << "};" << endl << endl;
+
+ // If we are generating templates, output a typedef for the plain
+ // factory name.
+ if (generator_->gen_templates_) {
+ f_header_ << "typedef " << factory_class_name_
+ << "< ::apache::thrift::protocol::TDummyProtocol > " << service_name_ << pstyle_
+ << "ProcessorFactory;" << endl << endl;
+ }
+
+ // Generate the getProcessor() method
+ f_out_ << template_header_ << indent() << "::std::shared_ptr< ::apache::thrift::"
+ << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
+ << factory_class_name_ << template_suffix_ << "::getProcessor("
+ << "const ::apache::thrift::TConnectionInfo& connInfo) {" << endl;
+ indent_up();
+
+ f_out_ << indent() << "::apache::thrift::ReleaseHandler< " << if_factory_name
+ << " > cleanup(handlerFactory_);" << endl << indent() << "::std::shared_ptr< "
+ << if_name_ << " > handler("
+ << "handlerFactory_->getHandler(connInfo), cleanup);" << endl << indent()
+ << "::std::shared_ptr< ::apache::thrift::"
+ << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
+ << "processor(new " << class_name_ << template_suffix_ << "(handler));" << endl << indent()
+ << "return processor;" << endl;
+
+ indent_down();
+ f_out_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates a service processor definition.
+ *
+ * @param tservice The service to generate a processor for.
+ */
+void t_cpp_generator::generate_service_processor(t_service* tservice, string style) {
+ ProcessorGenerator generator(this, tservice, style);
+ generator.run();
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_cpp_generator::generate_function_helpers(t_service* tservice, t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+
+ t_struct result(program_, tservice->get_name() + "_" + tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_struct_declaration(f_header_, &result, false);
+ generate_struct_definition(out, f_service_, &result, false);
+ generate_struct_reader(out, &result);
+ generate_struct_result_writer(out, &result);
+
+ result.set_name(tservice->get_name() + "_" + tfunction->get_name() + "_presult");
+ generate_struct_declaration(f_header_, &result, false, true, true, gen_cob_style_);
+ generate_struct_definition(out, f_service_, &result, false);
+ generate_struct_reader(out, &result, true);
+ if (gen_cob_style_) {
+ generate_struct_writer(out, &result, true);
+ }
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_cpp_generator::generate_process_function(t_service* tservice,
+ t_function* tfunction,
+ string style,
+ bool specialized) {
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ string service_func_name = "\"" + tservice->get_name() + "." + tfunction->get_name() + "\"";
+
+ std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+
+ string prot_type = (specialized ? "Protocol_" : "::apache::thrift::protocol::TProtocol");
+ string class_suffix;
+ if (gen_templates_) {
+ class_suffix = "T<Protocol_>";
+ }
+
+ // I tried to do this as one function. I really did. But it was too hard.
+ if (style != "Cob") {
+ // Open function
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl;
+ }
+ const bool unnamed_oprot_seqid = tfunction->is_oneway() && !(gen_templates_ && !specialized);
+ out << "void " << tservice->get_name() << "Processor" << class_suffix << "::"
+ << "process_" << tfunction->get_name() << "("
+ << "int32_t" << (unnamed_oprot_seqid ? ", " : " seqid, ") << prot_type << "* iprot, "
+ << prot_type << "*" << (unnamed_oprot_seqid ? ", " : " oprot, ") << "void* callContext)"
+ << endl;
+ scope_up(out);
+
+ string argsname = tservice->get_name() + "_" + tfunction->get_name() + "_args";
+ string resultname = tservice->get_name() + "_" + tfunction->get_name() + "_result";
+
+ if (tfunction->is_oneway() && !unnamed_oprot_seqid) {
+ out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl;
+ }
+
+ out << indent() << "void* ctx = NULL;" << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " ctx = this->eventHandler_->getContext(" << service_func_name << ", callContext);"
+ << endl << indent() << "}" << endl << indent()
+ << "::apache::thrift::TProcessorContextFreer freer("
+ << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl
+ << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->preRead(ctx, " << service_func_name << ");" << endl << indent()
+ << "}" << endl << endl << indent() << argsname << " args;" << endl << indent()
+ << "args.read(iprot);" << endl << indent() << "iprot->readMessageEnd();" << endl << indent()
+ << "uint32_t bytes = iprot->getTransport()->readEnd();" << endl << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl
+ << indent() << "}" << endl << endl;
+
+ // Declare result
+ if (!tfunction->is_oneway()) {
+ out << indent() << resultname << " result;" << endl;
+ }
+
+ // Try block for functions with exceptions
+ out << indent() << "try {" << endl;
+ indent_up();
+
+ // Generate the function call
+ bool first = true;
+ out << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ if (is_complex_type(tfunction->get_returntype())) {
+ first = false;
+ out << "iface_->" << tfunction->get_name() << "(result.success";
+ } else {
+ out << "result.success = iface_->" << tfunction->get_name() << "(";
+ }
+ } else {
+ out << "iface_->" << tfunction->get_name() << "(";
+ }
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ", ";
+ }
+ out << "args." << (*f_iter)->get_name();
+ }
+ out << ");" << endl;
+
+ // Set isset on success field
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ out << indent() << "result.__isset.success = true;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}";
+
+ if (!tfunction->is_oneway()) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name()
+ << ") {" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name()
+ << ";" << endl << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;"
+ << endl;
+ indent_down();
+ out << indent() << "}";
+ } else {
+ out << "}";
+ }
+ }
+ }
+
+ if (!tfunction->is_oneway()) {
+ out << " catch (const std::exception& e) {" << endl;
+ } else {
+ out << " catch (const std::exception&) {" << endl;
+ }
+
+ indent_up();
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl
+ << indent() << "}" << endl;
+
+ if (!tfunction->is_oneway()) {
+ out << endl << indent() << "::apache::thrift::TApplicationException x(e.what());" << endl
+ << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name()
+ << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl << indent()
+ << "x.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl
+ << indent() << "oprot->getTransport()->writeEnd();" << endl << indent()
+ << "oprot->getTransport()->flush();" << endl;
+ }
+ out << indent() << "return;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl
+ << indent() << "}" << endl << endl << indent() << "return;" << endl;
+ indent_down();
+ out << "}" << endl << endl;
+ return;
+ }
+
+ // Serialize the result into a struct
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl << indent()
+ << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name()
+ << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl << indent()
+ << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl
+ << indent() << "bytes = oprot->getTransport()->writeEnd();" << endl << indent()
+ << "oprot->getTransport()->flush();" << endl << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl
+ << indent() << "}" << endl;
+
+ // Close function
+ scope_down(out);
+ out << endl;
+ }
+
+ // Cob style.
+ else {
+ // Processor entry point.
+ // TODO(edhall) update for callContext when TEventServer is ready
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl;
+ }
+ out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::process_"
+ << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << prot_type << "* iprot, " << prot_type << "* oprot)" << endl;
+ scope_up(out);
+
+ // TODO(simpkins): we could try to consoldate this
+ // with the non-cob code above
+ if (gen_templates_ && !specialized) {
+ // If these are instances of Protocol_, instead of any old TProtocol,
+ // use the specialized process function instead.
+ out << indent() << "Protocol_* _iprot = dynamic_cast<Protocol_*>(iprot);" << endl << indent()
+ << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << endl << indent()
+ << "if (_iprot && _oprot) {" << endl << indent() << " return process_"
+ << tfunction->get_name() << "(cob, seqid, _iprot, _oprot);" << endl << indent() << "}"
+ << endl << indent() << "T_GENERIC_PROTOCOL(this, iprot, _iprot);" << endl << indent()
+ << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << endl << endl;
+ }
+
+ if (tfunction->is_oneway()) {
+ out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl;
+ }
+
+ out << indent() << tservice->get_name() + "_" + tfunction->get_name() << "_args args;" << endl
+ << indent() << "void* ctx = NULL;" << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl
+ << indent() << "}" << endl << indent() << "::apache::thrift::TProcessorContextFreer freer("
+ << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl
+ << indent() << "try {" << endl;
+ indent_up();
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->preRead(ctx, " << service_func_name << ");" << endl << indent()
+ << "}" << endl << indent() << "args.read(iprot);" << endl << indent()
+ << "iprot->readMessageEnd();" << endl << indent()
+ << "uint32_t bytes = iprot->getTransport()->readEnd();" << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl
+ << indent() << "}" << endl;
+ scope_down(out);
+
+ // TODO(dreiss): Handle TExceptions? Expose to server?
+ out << indent() << "catch (const std::exception&) {" << endl << indent()
+ << " if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl
+ << indent() << " }" << endl << indent() << " return cob(false);" << endl << indent()
+ << "}" << endl;
+
+ if (tfunction->is_oneway()) {
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl
+ << indent() << "}" << endl;
+ }
+ // TODO(dreiss): Figure out a strategy for exceptions in async handlers.
+ out << indent() << "freer.unregister();" << endl;
+ if (tfunction->is_oneway()) {
+ // No return. Just hand off our cob.
+ // TODO(dreiss): Call the cob immediately?
+ out << indent() << "iface_->" << tfunction->get_name() << "("
+ << "::std::bind(cob, true)" << endl;
+ indent_up();
+ indent_up();
+ } else {
+ string ret_arg, ret_placeholder;
+ if (!tfunction->get_returntype()->is_void()) {
+ ret_arg = ", const " + type_name(tfunction->get_returntype()) + "& _return";
+ ret_placeholder = ", ::std::placeholders::_1";
+ }
+
+ // When gen_templates_ is true, the return_ and throw_ functions are
+ // overloaded. We have to declare pointers to them so that the compiler
+ // can resolve the correct overloaded version.
+ out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix
+ << "::*return_fn)(::std::function<void(bool ok)> "
+ << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx" << ret_arg
+ << ") =" << endl;
+ out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix
+ << "::return_" << tfunction->get_name() << ";" << endl;
+ if (!xceptions.empty()) {
+ out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix
+ << "::*throw_fn)(::std::function<void(bool ok)> "
+ << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, "
+ << "::apache::thrift::TDelayedException* _throw) =" << endl;
+ out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix
+ << "::throw_" << tfunction->get_name() << ";" << endl;
+ }
+
+ out << indent() << "iface_->" << tfunction->get_name() << "(" << endl;
+ indent_up();
+ indent_up();
+ out << indent() << "::std::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder
+ << ")";
+ if (!xceptions.empty()) {
+ out << ',' << endl << indent() << "::std::bind(throw_fn, this, cob, seqid, oprot, "
+ << "ctx, ::std::placeholders::_1)";
+ }
+ }
+
+ // XXX Whitespace cleanup.
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ out << ',' << endl << indent() << "args." << (*f_iter)->get_name();
+ }
+ out << ");" << endl;
+ indent_down();
+ indent_down();
+ scope_down(out);
+ out << endl;
+
+ // Normal return.
+ if (!tfunction->is_oneway()) {
+ string ret_arg_decl, ret_arg_name;
+ if (!tfunction->get_returntype()->is_void()) {
+ ret_arg_decl = ", const " + type_name(tfunction->get_returntype()) + "& _return";
+ ret_arg_name = ", _return";
+ }
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl;
+ }
+ out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_"
+ << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << prot_type << "* oprot, void* ctx" << ret_arg_decl << ')' << endl;
+ scope_up(out);
+
+ if (gen_templates_ && !specialized) {
+ // If oprot is a Protocol_ instance,
+ // use the specialized return function instead.
+ out << indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << endl
+ << indent() << "if (_oprot) {" << endl << indent() << " return return_"
+ << tfunction->get_name() << "(cob, seqid, _oprot, ctx" << ret_arg_name << ");" << endl
+ << indent() << "}" << endl << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);"
+ << endl << endl;
+ }
+
+ out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_presult result;"
+ << endl;
+ if (!tfunction->get_returntype()->is_void()) {
+ // The const_cast here is unfortunate, but it would be a pain to avoid,
+ // and we only do a write with this struct, which is const-safe.
+ out << indent() << "result.success = const_cast<" << type_name(tfunction->get_returntype())
+ << "*>(&_return);" << endl << indent() << "result.__isset.success = true;" << endl;
+ }
+ // Serialize the result into a struct
+ out << endl << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl
+ << indent() << "}" << endl << indent()
+ << "::apache::thrift::TProcessorContextFreer freer("
+ << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl
+ << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl
+ << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\""
+ << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl
+ << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();"
+ << endl << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << endl
+ << indent() << "oprot->getTransport()->flush();" << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl
+ << indent() << "}" << endl << indent() << "return cob(true);" << endl;
+ scope_down(out);
+ out << endl;
+ }
+
+ // Exception return.
+ if (!tfunction->is_oneway() && !xceptions.empty()) {
+ if (gen_templates_) {
+ out << indent() << "template <class Protocol_>" << endl;
+ }
+ out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::throw_"
+ << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
+ << prot_type << "* oprot, void* ctx, "
+ << "::apache::thrift::TDelayedException* _throw)" << endl;
+ scope_up(out);
+
+ if (gen_templates_ && !specialized) {
+ // If oprot is a Protocol_ instance,
+ // use the specialized throw function instead.
+ out << indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << endl
+ << indent() << "if (_oprot) {" << endl << indent() << " return throw_"
+ << tfunction->get_name() << "(cob, seqid, _oprot, ctx, _throw);" << endl << indent()
+ << "}" << endl << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << endl
+ << endl;
+ }
+
+ // Get the event handler context
+ out << endl << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl
+ << indent() << "}" << endl << indent()
+ << "::apache::thrift::TProcessorContextFreer freer("
+ << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl;
+
+ // Throw the TDelayedException, and catch the result
+ out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_result result;"
+ << endl << endl << indent() << "try {" << endl;
+ indent_up();
+ out << indent() << "_throw->throw_it();" << endl << indent() << "return cob(false);"
+ << endl; // Is this possible? TBD.
+ indent_down();
+ out << indent() << '}';
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name()
+ << ") {" << endl;
+ indent_up();
+ out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name()
+ << ";" << endl << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;"
+ << endl;
+ scope_down(out);
+ }
+
+ // Handle the case where an undeclared exception is thrown
+ out << " catch (std::exception& e) {" << endl;
+ indent_up();
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl
+ << indent() << "}" << endl << endl << indent()
+ << "::apache::thrift::TApplicationException x(e.what());" << endl << indent()
+ << "oprot->writeMessageBegin(\"" << tfunction->get_name()
+ << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl << indent()
+ << "x.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl
+ << indent() << "oprot->getTransport()->writeEnd();" << endl << indent()
+ << "oprot->getTransport()->flush();" << endl <<
+ // We pass true to the cob here, since we did successfully write a
+ // response, even though it is an exception response.
+ // It looks like the argument is currently ignored, anyway.
+ indent() << "return cob(true);" << endl;
+ scope_down(out);
+
+ // Serialize the result into a struct
+ out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << endl
+ << indent() << "}" << endl << endl << indent() << "oprot->writeMessageBegin(\""
+ << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << endl
+ << indent() << "result.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();"
+ << endl << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << endl
+ << indent() << "oprot->getTransport()->flush();" << endl << indent()
+ << "if (this->eventHandler_.get() != NULL) {" << endl << indent()
+ << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl
+ << indent() << "}" << endl << indent() << "return cob(true);" << endl;
+ scope_down(out);
+ out << endl;
+ } // for each function
+ } // cob style
+}
+
+/**
+ * Generates a skeleton file of a server
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_cpp_generator::generate_service_skeleton(t_service* tservice) {
+ string svcname = tservice->get_name();
+
+ // Service implementation file includes
+ string f_skeleton_name = get_out_dir() + svcname + "_server.skeleton.cpp";
+
+ string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp"));
+
+ ofstream_with_content_based_conditional_update f_skeleton;
+ f_skeleton.open(f_skeleton_name.c_str());
+ f_skeleton << "// This autogenerated skeleton file illustrates how to build a server." << endl
+ << "// You should copy it to another filename to avoid overwriting it." << endl << endl
+ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl
+ << "#include <thrift/protocol/TBinaryProtocol.h>" << endl
+ << "#include <thrift/server/TSimpleServer.h>" << endl
+ << "#include <thrift/transport/TServerSocket.h>" << endl
+ << "#include <thrift/transport/TBufferTransports.h>" << endl << endl
+ << "using namespace ::apache::thrift;" << endl
+ << "using namespace ::apache::thrift::protocol;" << endl
+ << "using namespace ::apache::thrift::transport;" << endl
+ << "using namespace ::apache::thrift::server;" << endl << endl;
+
+ // the following code would not compile:
+ // using namespace ;
+ // using namespace ::;
+ if ((!ns.empty()) && (ns.compare(" ::") != 0)) {
+ f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << endl << endl;
+ }
+
+ f_skeleton << "class " << svcname << "Handler : virtual public " << svcname << "If {" << endl
+ << " public:" << endl;
+ indent_up();
+ f_skeleton << indent() << svcname << "Handler() {" << endl << indent()
+ << " // Your initialization goes here" << endl << indent() << "}" << endl << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_java_doc(f_skeleton, *f_iter);
+ f_skeleton << indent() << function_signature(*f_iter, "") << " {" << endl << indent()
+ << " // Your implementation goes here" << endl << indent() << " printf(\""
+ << (*f_iter)->get_name() << "\\n\");" << endl << indent() << "}" << endl << endl;
+ }
+
+ indent_down();
+ f_skeleton << "};" << endl << endl;
+
+ f_skeleton << indent() << "int main(int argc, char **argv) {" << endl;
+ indent_up();
+ f_skeleton
+ << indent() << "int port = 9090;" << endl << indent() << "::std::shared_ptr<" << svcname
+ << "Handler> handler(new " << svcname << "Handler());" << endl << indent()
+ << "::std::shared_ptr<TProcessor> processor(new " << svcname << "Processor(handler));" << endl
+ << indent() << "::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));"
+ << endl << indent()
+ << "::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());" << endl
+ << indent() << "::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());"
+ << endl << endl << indent()
+ << "TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);"
+ << endl << indent() << "server.serve();" << endl << indent() << "return 0;" << endl;
+ indent_down();
+ f_skeleton << "}" << endl << endl;
+
+ // Close the files
+ f_skeleton.close();
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_cpp_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ string suffix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name() + suffix;
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name, is_reference(tfield));
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type()) {
+ indent(out) << "xfer += iprot->";
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "readBinary(" << name << ");";
+ } else {
+ out << "readString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + name;
+ }
+ out << endl;
+ } else if (type->is_enum()) {
+ string t = tmp("ecast");
+ out << indent() << "int32_t " << t << ";" << endl << indent() << "xfer += iprot->readI32(" << t
+ << ");" << endl << indent() << name << " = (" << type_name(type) << ")" << t << ";" << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a variable. This makes two key assumptions,
+ * first that there is a const char* variable named data that points to the
+ * buffer for deserialization, and that there is a variable protocol which
+ * is a reference to a TProtocol serialization object.
+ */
+void t_cpp_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix,
+ bool pointer) {
+ if (pointer) {
+ indent(out) << "if (!" << prefix << ") { " << endl;
+ indent(out) << " " << prefix << " = ::std::shared_ptr<" << type_name(tstruct) << ">(new "
+ << type_name(tstruct) << ");" << endl;
+ indent(out) << "}" << endl;
+ indent(out) << "xfer += " << prefix << "->read(iprot);" << endl;
+ indent(out) << "bool wasSet = false;" << endl;
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) {
+
+ indent(out) << "if (" << prefix << "->__isset." << (*f_iter)->get_name()
+ << ") { wasSet = true; }" << endl;
+ }
+ indent(out) << "if (!wasSet) { " << prefix << ".reset(); }" << endl;
+ } else {
+ indent(out) << "xfer += " << prefix << ".read(iprot);" << endl;
+ }
+}
+
+void t_cpp_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+
+ t_container* tcontainer = (t_container*)ttype;
+ bool use_push = tcontainer->has_cpp_name();
+
+ indent(out) << prefix << ".clear();" << endl << indent() << "uint32_t " << size << ";" << endl;
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ out << indent() << "::apache::thrift::protocol::TType " << ktype << ";" << endl << indent()
+ << "::apache::thrift::protocol::TType " << vtype << ";" << endl << indent()
+ << "xfer += iprot->readMapBegin(" << ktype << ", " << vtype << ", " << size << ");" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "::apache::thrift::protocol::TType " << etype << ";" << endl << indent()
+ << "xfer += iprot->readSetBegin(" << etype << ", " << size << ");" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "::apache::thrift::protocol::TType " << etype << ";" << endl << indent()
+ << "xfer += iprot->readListBegin(" << etype << ", " << size << ");" << endl;
+ if (!use_push) {
+ indent(out) << prefix << ".resize(" << size << ");" << endl;
+ }
+ }
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ out << indent() << "uint32_t " << i << ";" << endl << indent() << "for (" << i << " = 0; " << i
+ << " < " << size << "; ++" << i << ")" << endl;
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix, use_push, i);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "xfer += iprot->readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "xfer += iprot->readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "xfer += iprot->readListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_cpp_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ out << indent() << declare_field(&fkey) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ indent(out) << declare_field(&fval, false, false, false, true) << " = " << prefix << "[" << key
+ << "];" << endl;
+
+ generate_deserialize_field(out, &fval);
+}
+
+void t_cpp_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".insert(" << elem << ");" << endl;
+}
+
+void t_cpp_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix,
+ bool use_push,
+ string index) {
+ if (use_push) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+ indent(out) << declare_field(&felem) << endl;
+ generate_deserialize_field(out, &felem);
+ indent(out) << prefix << ".push_back(" << elem << ");" << endl;
+ } else {
+ t_field felem(tlist->get_elem_type(), prefix + "[" + index + "]");
+ generate_deserialize_field(out, &felem);
+ }
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_cpp_generator::generate_serialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ string suffix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ string name = prefix + tfield->get_name() + suffix;
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name, is_reference(tfield));
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent(out) << "xfer += oprot->";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ");";
+ } else {
+ out << "writeString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no C++ writer for base type " + t_base_type::t_base_name(tbase)
+ + name;
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32((int32_t)" << name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
+ name.c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_cpp_generator::generate_serialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix,
+ bool pointer) {
+ if (pointer) {
+ indent(out) << "if (" << prefix << ") {" << endl;
+ indent(out) << " xfer += " << prefix << "->write(oprot); " << endl;
+ indent(out) << "} else {"
+ << "oprot->writeStructBegin(\"" << tstruct->get_name() << "\"); " << endl;
+ indent(out) << " oprot->writeStructEnd();" << endl;
+ indent(out) << " oprot->writeFieldStop();" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "xfer += " << prefix << ".write(oprot);" << endl;
+ }
+}
+
+void t_cpp_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "xfer += oprot->writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "static_cast<uint32_t>(" << prefix << ".size()));" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "xfer += oprot->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", "
+ << "static_cast<uint32_t>(" << prefix << ".size()));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "xfer += oprot->writeListBegin("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", "
+ << "static_cast<uint32_t>(" << prefix << ".size()));" << endl;
+ }
+
+ string iter = tmp("_iter");
+ out << indent() << type_name(ttype) << "::const_iterator " << iter << ";" << endl << indent()
+ << "for (" << iter << " = " << prefix << ".begin(); " << iter << " != " << prefix
+ << ".end(); ++" << iter << ")" << endl;
+ scope_up(out);
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ }
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "xfer += oprot->writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "xfer += oprot->writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "xfer += oprot->writeListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_cpp_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter) {
+ t_field kfield(tmap->get_key_type(), iter + "->first");
+ generate_serialize_field(out, &kfield, "");
+
+ t_field vfield(tmap->get_val_type(), iter + "->second");
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_cpp_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), "(*" + iter + ")");
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_cpp_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), "(*" + iter + ")");
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Makes a :: prefix for a namespace
+ *
+ * @param ns The namespace, w/ periods in it
+ * @return Namespaces
+ */
+string t_cpp_generator::namespace_prefix(string ns) {
+ // Always start with "::", to avoid possible name collisions with
+ // other names in one of the current namespaces.
+ //
+ // We also need a leading space, in case the name is used inside of a
+ // template parameter. "MyTemplate<::foo::Bar>" is not valid C++,
+ // since "<:" is an alternative token for "[".
+ string result = " ::";
+
+ if (ns.size() == 0) {
+ return result;
+ }
+ string::size_type loc;
+ while ((loc = ns.find(".")) != string::npos) {
+ result += ns.substr(0, loc);
+ result += "::";
+ ns = ns.substr(loc + 1);
+ }
+ if (ns.size() > 0) {
+ result += ns + "::";
+ }
+ return result;
+}
+
+/**
+ * Opens namespace.
+ *
+ * @param ns The namespace, w/ periods in it
+ * @return Namespaces
+ */
+string t_cpp_generator::namespace_open(string ns) {
+ if (ns.size() == 0) {
+ return "";
+ }
+ string result = "";
+ string separator = "";
+ string::size_type loc;
+ while ((loc = ns.find(".")) != string::npos) {
+ result += separator;
+ result += "namespace ";
+ result += ns.substr(0, loc);
+ result += " {";
+ separator = " ";
+ ns = ns.substr(loc + 1);
+ }
+ if (ns.size() > 0) {
+ result += separator + "namespace " + ns + " {";
+ }
+ return result;
+}
+
+/**
+ * Closes namespace.
+ *
+ * @param ns The namespace, w/ periods in it
+ * @return Namespaces
+ */
+string t_cpp_generator::namespace_close(string ns) {
+ if (ns.size() == 0) {
+ return "";
+ }
+ string result = "}";
+ string::size_type loc;
+ while ((loc = ns.find(".")) != string::npos) {
+ result += "}";
+ ns = ns.substr(loc + 1);
+ }
+ result += " // namespace";
+ return result;
+}
+
+/**
+ * Returns a C++ type name
+ *
+ * @param ttype The type
+ * @return String of the type name, i.e. std::set<type>
+ */
+string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) {
+ if (ttype->is_base_type()) {
+ string bname = base_type_name(((t_base_type*)ttype)->get_base());
+ std::map<string, string>::iterator it = ttype->annotations_.find("cpp.type");
+ if (it != ttype->annotations_.end()) {
+ bname = it->second;
+ }
+
+ if (!arg) {
+ return bname;
+ }
+
+ if (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING) {
+ return "const " + bname + "&";
+ } else {
+ return "const " + bname;
+ }
+ }
+
+ // Check for a custom overloaded C++ name
+ if (ttype->is_container()) {
+ string cname;
+
+ t_container* tcontainer = (t_container*)ttype;
+ if (tcontainer->has_cpp_name()) {
+ cname = tcontainer->get_cpp_name();
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ cname = "std::map<" + type_name(tmap->get_key_type(), in_typedef) + ", "
+ + type_name(tmap->get_val_type(), in_typedef) + "> ";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ cname = "std::set<" + type_name(tset->get_elem_type(), in_typedef) + "> ";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ cname = "std::vector<" + type_name(tlist->get_elem_type(), in_typedef) + "> ";
+ }
+
+ if (arg) {
+ return "const " + cname + "&";
+ } else {
+ return cname;
+ }
+ }
+
+ string class_prefix;
+ if (in_typedef && (ttype->is_struct() || ttype->is_xception())) {
+ class_prefix = "class ";
+ }
+
+ // Check if it needs to be namespaced
+ string pname;
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_) {
+ pname = class_prefix + namespace_prefix(program->get_namespace("cpp")) + ttype->get_name();
+ } else {
+ pname = class_prefix + ttype->get_name();
+ }
+
+ if (ttype->is_enum() && !gen_pure_enums_) {
+ pname += "::type";
+ }
+
+ if (arg) {
+ if (is_complex_type(ttype)) {
+ return "const " + pname + "&";
+ } else {
+ return "const " + pname;
+ }
+ } else {
+ return pname;
+ }
+}
+
+/**
+ * Returns the C++ type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ * @return Explicit C++ type, i.e. "int32_t"
+ */
+string t_cpp_generator::base_type_name(t_base_type::t_base tbase) {
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ return "std::string";
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "int8_t";
+ case t_base_type::TYPE_I16:
+ return "int16_t";
+ case t_base_type::TYPE_I32:
+ return "int32_t";
+ case t_base_type::TYPE_I64:
+ return "int64_t";
+ case t_base_type::TYPE_DOUBLE:
+ return "double";
+ default:
+ throw "compiler error: no C++ base type name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ * @return Field declaration, i.e. int x = 0;
+ */
+string t_cpp_generator::declare_field(t_field* tfield,
+ bool init,
+ bool pointer,
+ bool constant,
+ bool reference) {
+ // TODO(mcslee): do we ever need to initialize the field?
+ string result = "";
+ if (constant) {
+ result += "const ";
+ }
+ result += type_name(tfield->get_type());
+ if (is_reference(tfield)) {
+ result = "::std::shared_ptr<" + result + ">";
+ }
+ if (pointer) {
+ result += "*";
+ }
+ if (reference) {
+ result += "&";
+ }
+ result += " " + tfield->get_name();
+ if (init) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ case t_base_type::TYPE_STRING:
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ default:
+ throw "compiler error: no C++ initializer for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ result += " = (" + type_name(type) + ")0";
+ }
+ }
+ if (!reference) {
+ result += ";";
+ }
+ return result;
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_cpp_generator::function_signature(t_function* tfunction,
+ string style,
+ string prefix,
+ bool name_params) {
+ t_type* ttype = tfunction->get_returntype();
+ t_struct* arglist = tfunction->get_arglist();
+ bool has_xceptions = !tfunction->get_xceptions()->get_members().empty();
+
+ if (style == "") {
+ if (is_complex_type(ttype)) {
+ return "void " + prefix + tfunction->get_name() + "(" + type_name(ttype)
+ + (name_params ? "& _return" : "& /* _return */")
+ + argument_list(arglist, name_params, true) + ")";
+ } else {
+ return type_name(ttype) + " " + prefix + tfunction->get_name() + "("
+ + argument_list(arglist, name_params) + ")";
+ }
+ } else if (style.substr(0, 3) == "Cob") {
+ string cob_type;
+ string exn_cob;
+ if (style == "CobCl") {
+ cob_type = "(" + service_name_ + "CobClient";
+ if (gen_templates_) {
+ cob_type += "T<Protocol_>";
+ }
+ cob_type += "* client)";
+ } else if (style == "CobSv") {
+ cob_type = (ttype->is_void() ? "()" : ("(" + type_name(ttype) + " const& _return)"));
+ if (has_xceptions) {
+ exn_cob
+ = ", ::std::function<void(::apache::thrift::TDelayedException* _throw)> /* exn_cob */";
+ }
+ } else {
+ throw "UNKNOWN STYLE";
+ }
+
+ return "void " + prefix + tfunction->get_name() + "(::std::function<void" + cob_type + "> cob"
+ + exn_cob + argument_list(arglist, name_params, true) + ")";
+ } else {
+ throw "UNKNOWN STYLE";
+ }
+}
+
+/**
+ * Renders a field list
+ *
+ * @param tstruct The struct definition
+ * @return Comma sepearated list of all field names in that struct
+ */
+string t_cpp_generator::argument_list(t_struct* tstruct, bool name_params, bool start_comma) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = !start_comma;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type(), false, true) + " "
+ + (name_params ? (*f_iter)->get_name() : "/* " + (*f_iter)->get_name() + " */");
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ *
+ * @param type Thrift Type
+ * @return String of C++ code to definition of that type constant
+ */
+string t_cpp_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "::apache::thrift::protocol::T_STRING";
+ case t_base_type::TYPE_BOOL:
+ return "::apache::thrift::protocol::T_BOOL";
+ case t_base_type::TYPE_I8:
+ return "::apache::thrift::protocol::T_BYTE";
+ case t_base_type::TYPE_I16:
+ return "::apache::thrift::protocol::T_I16";
+ case t_base_type::TYPE_I32:
+ return "::apache::thrift::protocol::T_I32";
+ case t_base_type::TYPE_I64:
+ return "::apache::thrift::protocol::T_I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "::apache::thrift::protocol::T_DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "::apache::thrift::protocol::T_I32";
+ } else if (type->is_struct()) {
+ return "::apache::thrift::protocol::T_STRUCT";
+ } else if (type->is_xception()) {
+ return "::apache::thrift::protocol::T_STRUCT";
+ } else if (type->is_map()) {
+ return "::apache::thrift::protocol::T_MAP";
+ } else if (type->is_set()) {
+ return "::apache::thrift::protocol::T_SET";
+ } else if (type->is_list()) {
+ return "::apache::thrift::protocol::T_LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+string t_cpp_generator::get_include_prefix(const t_program& program) const {
+ string include_prefix = program.get_include_prefix();
+ if (!use_include_prefix_ || (include_prefix.size() > 0 && include_prefix[0] == '/')) {
+ // if flag is turned off or this is absolute path, return empty prefix
+ return "";
+ }
+
+ string::size_type last_slash = string::npos;
+ if ((last_slash = include_prefix.rfind("/")) != string::npos) {
+ return include_prefix.substr(0, last_slash)
+ + (get_program()->is_out_path_absolute() ? "/" : "/" + out_dir_base_ + "/");
+ }
+
+ return "";
+}
+
+THRIFT_REGISTER_GENERATOR(
+ cpp,
+ "C++",
+ " cob_style: Generate \"Continuation OBject\"-style classes.\n"
+ " no_client_completion:\n"
+ " Omit calls to completion__() in CobClient class.\n"
+ " no_default_operators:\n"
+ " Omits generation of default operators ==, != and <\n"
+ " templates: Generate templatized reader/writer methods.\n"
+ " pure_enums: Generate pure enums instead of wrapper classes.\n"
+ " include_prefix: Use full include paths in generated files.\n"
+ " moveable_types: Generate move constructors and assignment operators.\n"
+ " no_ostream_operators:\n"
+ " Omit generation of ostream definitions.\n"
+ " no_skeleton: Omits generation of skeleton.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc
new file mode 100644
index 000000000..a6ece8083
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc
@@ -0,0 +1,3261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+struct member_mapping_scope {
+ void* scope_member;
+ std::map<std::string, std::string> mapping_table;
+};
+
+class t_csharp_generator : public t_oop_generator {
+public:
+ t_csharp_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+
+ std::map<std::string, std::string>::const_iterator iter;
+
+ async_ = false;
+ nullable_ = false;
+ hashcode_ = false;
+ union_ = false;
+ serialize_ = false;
+ wcf_ = false;
+ wcf_namespace_.clear();
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("async") == 0) {
+ async_ = true;
+ } else if( iter->first.compare("nullable") == 0) {
+ nullable_ = true;
+ } else if( iter->first.compare("hashcode") == 0) {
+ hashcode_ = true;
+ } else if( iter->first.compare("union") == 0) {
+ union_ = true;
+ } else if( iter->first.compare("serial") == 0) {
+ serialize_ = true;
+ wcf_namespace_ = iter->second; // since there can be only one namespace
+ } else if( iter->first.compare("wcf") == 0) {
+ wcf_ = true;
+ wcf_namespace_ = iter->second;
+ } else {
+ throw "unknown option csharp:" + iter->first;
+ }
+ }
+
+ pwarning(1, "The 'csharp' target is deprecated. Consider using 'netstd' instead.\n");
+
+ out_dir_base_ = "gen-csharp";
+ }
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_union(t_struct* tunion);
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+ void generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset);
+ void generate_csharp_property(ostream& out,
+ t_field* tfield,
+ bool isPublic,
+ bool includeIsset = true,
+ std::string fieldPrefix = "");
+ bool print_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval = false,
+ bool needtype = false);
+ std::string render_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+ void print_const_constructor(std::ostream& out, std::vector<t_const*> consts);
+ void print_const_def_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+
+ void generate_csharp_struct(t_struct* tstruct, bool is_exception);
+ void generate_csharp_union(t_struct* tunion);
+ void generate_csharp_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool in_class = false,
+ bool is_result = false);
+ void generate_csharp_union_definition(std::ostream& out, t_struct* tunion);
+ void generate_csharp_union_class(std::ostream& out, t_struct* tunion, t_field* tfield);
+ void generate_csharp_wcffault(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_struct_tostring(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_struct_equals(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_struct_hashcode(std::ostream& out, t_struct* tstruct);
+ void generate_csharp_union_reader(std::ostream& out, t_struct* tunion);
+
+ void generate_function_helpers(t_function* tfunction);
+ void generate_service_interface(t_service* tservice);
+ void generate_separate_service_interfaces(t_service* tservice);
+ void generate_sync_service_interface(t_service* tservice);
+ void generate_async_service_interface(t_service* tservice);
+ void generate_combined_service_interface(t_service* tservice);
+ void generate_silverlight_async_methods(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_service_server_sync(t_service* tservice);
+ void generate_service_server_async(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* function);
+ void generate_process_function_async(t_service* tservice, t_function* function);
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool is_propertyless = false);
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+ void generate_deserialize_list_element(std::ostream& out, t_list* list, std::string prefix = "");
+ void generate_serialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool is_element = false,
+ bool is_propertyless = false);
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string iter,
+ std::string map);
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_csharp_doc(std::ostream& out, t_field* field);
+ void generate_csharp_doc(std::ostream& out, t_doc* tdoc);
+ void generate_csharp_doc(std::ostream& out, t_function* tdoc);
+ void generate_csharp_docstring_comment(std::ostream& out, string contents);
+
+ void start_csharp_namespace(std::ostream& out);
+ void end_csharp_namespace(std::ostream& out);
+
+ std::string csharp_type_usings();
+ std::string csharp_thrift_usings();
+
+ std::string type_name(t_type* ttype,
+ bool in_countainer = false,
+ bool in_init = false,
+ bool in_param = false,
+ bool is_required = false);
+ std::string base_type_name(t_base_type* tbase,
+ bool in_container = false,
+ bool in_param = false,
+ bool is_required = false);
+ std::string declare_field(t_field* tfield, bool init = false, std::string prefix = "");
+ std::string function_signature_async_begin(t_function* tfunction, std::string prefix = "");
+ std::string function_signature_async_end(t_function* tfunction, std::string prefix = "");
+ std::string function_signature_async(t_function* tfunction, std::string prefix = "");
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string prop_name(t_field* tfield, bool suppress_mapping = false);
+ std::string get_enum_class_name(t_type* type) override;
+
+ bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; }
+
+ bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; }
+
+ bool type_can_be_null(t_type* ttype) {
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception()
+ || ttype->is_string();
+ }
+
+private:
+ std::string namespace_name_;
+ ofstream_with_content_based_conditional_update f_service_;
+ std::string namespace_dir_;
+ bool async_;
+ bool nullable_;
+ bool union_;
+ bool hashcode_;
+ bool serialize_;
+ bool wcf_;
+ std::string wcf_namespace_;
+
+ std::map<std::string, int> csharp_keywords;
+ std::vector<member_mapping_scope> member_mapping_scopes;
+
+ void init_keywords();
+ std::string normalize_name(std::string name);
+ std::string make_valid_csharp_identifier(std::string const& fromName);
+ void prepare_member_name_mapping(t_struct* tstruct);
+ void prepare_member_name_mapping(void* scope,
+ const vector<t_field*>& members,
+ const string& structname);
+ void cleanup_member_name_mapping(void* scope);
+ string get_mapped_member_name(string oldname);
+};
+
+void t_csharp_generator::init_generator() {
+ MKDIR(get_out_dir().c_str());
+ namespace_name_ = program_->get_namespace("csharp");
+
+ string dir = namespace_name_;
+ string subdir = get_out_dir().c_str();
+ string::size_type loc;
+
+ while ((loc = dir.find(".")) != string::npos) {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0) {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ namespace_dir_ = subdir;
+ init_keywords();
+
+ while( ! member_mapping_scopes.empty()) {
+ cleanup_member_name_mapping( member_mapping_scopes.back().scope_member);
+ }
+
+ pverbose("C# options:\n");
+ pverbose("- async ...... %s\n", (async_ ? "ON" : "off"));
+ pverbose("- nullable ... %s\n", (nullable_ ? "ON" : "off"));
+ pverbose("- union ...... %s\n", (union_ ? "ON" : "off"));
+ pverbose("- hashcode ... %s\n", (hashcode_ ? "ON" : "off"));
+ pverbose("- serialize .. %s\n", (serialize_ ? "ON" : "off"));
+ pverbose("- wcf ........ %s\n", (wcf_ ? "ON" : "off"));
+}
+
+std::string t_csharp_generator::normalize_name(std::string name) {
+ string tmp(name);
+ std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int (*)(int)>(std::tolower));
+
+ // un-conflict keywords by prefixing with "@"
+ if (csharp_keywords.find(tmp) != csharp_keywords.end()) {
+ return "@" + name;
+ }
+
+ // no changes necessary
+ return name;
+}
+
+void t_csharp_generator::init_keywords() {
+ csharp_keywords.clear();
+
+ // C# keywords
+ csharp_keywords["abstract"] = 1;
+ csharp_keywords["as"] = 1;
+ csharp_keywords["base"] = 1;
+ csharp_keywords["bool"] = 1;
+ csharp_keywords["break"] = 1;
+ csharp_keywords["byte"] = 1;
+ csharp_keywords["case"] = 1;
+ csharp_keywords["catch"] = 1;
+ csharp_keywords["char"] = 1;
+ csharp_keywords["checked"] = 1;
+ csharp_keywords["class"] = 1;
+ csharp_keywords["const"] = 1;
+ csharp_keywords["continue"] = 1;
+ csharp_keywords["decimal"] = 1;
+ csharp_keywords["default"] = 1;
+ csharp_keywords["delegate"] = 1;
+ csharp_keywords["do"] = 1;
+ csharp_keywords["double"] = 1;
+ csharp_keywords["else"] = 1;
+ csharp_keywords["enum"] = 1;
+ csharp_keywords["event"] = 1;
+ csharp_keywords["explicit"] = 1;
+ csharp_keywords["extern"] = 1;
+ csharp_keywords["false"] = 1;
+ csharp_keywords["finally"] = 1;
+ csharp_keywords["fixed"] = 1;
+ csharp_keywords["float"] = 1;
+ csharp_keywords["for"] = 1;
+ csharp_keywords["foreach"] = 1;
+ csharp_keywords["goto"] = 1;
+ csharp_keywords["if"] = 1;
+ csharp_keywords["implicit"] = 1;
+ csharp_keywords["in"] = 1;
+ csharp_keywords["int"] = 1;
+ csharp_keywords["interface"] = 1;
+ csharp_keywords["internal"] = 1;
+ csharp_keywords["is"] = 1;
+ csharp_keywords["lock"] = 1;
+ csharp_keywords["long"] = 1;
+ csharp_keywords["namespace"] = 1;
+ csharp_keywords["new"] = 1;
+ csharp_keywords["null"] = 1;
+ csharp_keywords["object"] = 1;
+ csharp_keywords["operator"] = 1;
+ csharp_keywords["out"] = 1;
+ csharp_keywords["override"] = 1;
+ csharp_keywords["params"] = 1;
+ csharp_keywords["private"] = 1;
+ csharp_keywords["protected"] = 1;
+ csharp_keywords["public"] = 1;
+ csharp_keywords["readonly"] = 1;
+ csharp_keywords["ref"] = 1;
+ csharp_keywords["return"] = 1;
+ csharp_keywords["sbyte"] = 1;
+ csharp_keywords["sealed"] = 1;
+ csharp_keywords["short"] = 1;
+ csharp_keywords["sizeof"] = 1;
+ csharp_keywords["stackalloc"] = 1;
+ csharp_keywords["static"] = 1;
+ csharp_keywords["string"] = 1;
+ csharp_keywords["struct"] = 1;
+ csharp_keywords["switch"] = 1;
+ csharp_keywords["this"] = 1;
+ csharp_keywords["throw"] = 1;
+ csharp_keywords["true"] = 1;
+ csharp_keywords["try"] = 1;
+ csharp_keywords["typeof"] = 1;
+ csharp_keywords["uint"] = 1;
+ csharp_keywords["ulong"] = 1;
+ csharp_keywords["unchecked"] = 1;
+ csharp_keywords["unsafe"] = 1;
+ csharp_keywords["ushort"] = 1;
+ csharp_keywords["using"] = 1;
+ csharp_keywords["virtual"] = 1;
+ csharp_keywords["void"] = 1;
+ csharp_keywords["volatile"] = 1;
+ csharp_keywords["while"] = 1;
+
+ // C# contextual keywords
+ csharp_keywords["add"] = 1;
+ csharp_keywords["alias"] = 1;
+ csharp_keywords["ascending"] = 1;
+ csharp_keywords["async"] = 1;
+ csharp_keywords["await"] = 1;
+ csharp_keywords["descending"] = 1;
+ csharp_keywords["dynamic"] = 1;
+ csharp_keywords["from"] = 1;
+ csharp_keywords["get"] = 1;
+ csharp_keywords["global"] = 1;
+ csharp_keywords["group"] = 1;
+ csharp_keywords["into"] = 1;
+ csharp_keywords["join"] = 1;
+ csharp_keywords["let"] = 1;
+ csharp_keywords["orderby"] = 1;
+ csharp_keywords["partial"] = 1;
+ csharp_keywords["remove"] = 1;
+ csharp_keywords["select"] = 1;
+ csharp_keywords["set"] = 1;
+ csharp_keywords["value"] = 1;
+ csharp_keywords["var"] = 1;
+ csharp_keywords["where"] = 1;
+ csharp_keywords["yield"] = 1;
+}
+
+void t_csharp_generator::start_csharp_namespace(ostream& out) {
+ if (!namespace_name_.empty()) {
+ out << "namespace " << namespace_name_ << "\n";
+ scope_up(out);
+ }
+}
+
+void t_csharp_generator::end_csharp_namespace(ostream& out) {
+ if (!namespace_name_.empty()) {
+ scope_down(out);
+ }
+}
+
+string t_csharp_generator::csharp_type_usings() {
+ return string() + "using System;\n" + "using System.Collections;\n"
+ + "using System.Collections.Generic;\n" + "using System.Text;\n" + "using System.IO;\n"
+ + ((async_) ? "using System.Threading.Tasks;\n" : "") + "using Thrift;\n"
+ + "using Thrift.Collections;\n" + ((serialize_ || wcf_) ? "#if !SILVERLIGHT\n" : "")
+ + ((serialize_ || wcf_) ? "using System.Xml.Serialization;\n" : "")
+ + ((serialize_ || wcf_) ? "#endif\n" : "")
+ + "using System.Runtime.Serialization;\n";
+}
+
+string t_csharp_generator::csharp_thrift_usings() {
+ return string() + "using Thrift.Protocol;\n" + "using Thrift.Transport;\n";
+}
+
+void t_csharp_generator::close_generator() {
+}
+void t_csharp_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+void t_csharp_generator::generate_enum(t_enum* tenum) {
+ string f_enum_name = namespace_dir_ + "/" + (tenum->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ f_enum << autogen_comment() << endl;
+
+ start_csharp_namespace(f_enum);
+
+ generate_csharp_doc(f_enum, tenum);
+
+ indent(f_enum) << "public enum " << tenum->get_name() << "\n";
+ scope_up(f_enum);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ generate_csharp_doc(f_enum, *c_iter);
+
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << (*c_iter)->get_name() << " = " << value << "," << endl;
+ }
+
+ scope_down(f_enum);
+
+ end_csharp_namespace(f_enum);
+
+ f_enum.close();
+}
+
+void t_csharp_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+ string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ f_consts << autogen_comment() << csharp_type_usings() << endl;
+
+ start_csharp_namespace(f_consts);
+
+ indent(f_consts) << "public static class " << make_valid_csharp_identifier(program_name_)
+ << "Constants" << endl;
+ scope_up(f_consts);
+
+ vector<t_const*>::iterator c_iter;
+ bool need_static_constructor = false;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ generate_csharp_doc(f_consts, (*c_iter));
+ if (print_const_value(f_consts,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ false)) {
+ need_static_constructor = true;
+ }
+ }
+
+ if (need_static_constructor) {
+ print_const_constructor(f_consts, consts);
+ }
+
+ scope_down(f_consts);
+ end_csharp_namespace(f_consts);
+ f_consts.close();
+}
+
+void t_csharp_generator::print_const_def_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ prepare_member_name_mapping((t_struct*)type);
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_field* field = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field = (*f_iter);
+ }
+ }
+ if (field == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ t_type* field_type = field->get_type();
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ indent(out) << name << "." << prop_name(field) << " = " << val << ";" << endl;
+ }
+ cleanup_member_name_mapping((t_struct*)type);
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ indent(out) << name << "[" << key << "]"
+ << " = " << val << ";" << endl;
+ }
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ indent(out) << name << ".Add(" << val << ");" << endl;
+ }
+ }
+}
+
+void t_csharp_generator::print_const_constructor(std::ostream& out, std::vector<t_const*> consts) {
+ indent(out) << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()"
+ << endl;
+ scope_up(out);
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ string name = (*c_iter)->get_name();
+ t_type* type = (*c_iter)->get_type();
+ t_const_value* value = (*c_iter)->get_value();
+
+ print_const_def_value(out, name, type, value);
+ }
+ scope_down(out);
+}
+
+// it seems like all that methods that call this are using in_static to be the opposite of what it
+// would imply
+bool t_csharp_generator::print_const_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval,
+ bool needtype) {
+ indent(out);
+ bool need_static_construction = !in_static;
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (!defval || needtype) {
+ out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ")
+ << type_name(type) << " ";
+ }
+ if (type->is_base_type()) {
+ string v2 = render_const_value(out, name, type, value);
+ out << name << " = " << v2 << ";" << endl;
+ need_static_construction = false;
+ } else if (type->is_enum()) {
+ out << name << " = " << type_name(type, false, true) << "." << value->get_identifier_name()
+ << ";" << endl;
+ need_static_construction = false;
+ } else if (type->is_struct() || type->is_xception()) {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ } else if (type->is_map()) {
+ out << name << " = new " << type_name(type, true, true) << "();" << endl;
+ } else if (type->is_list() || type->is_set()) {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ }
+
+ if (defval && !type->is_base_type() && !type->is_enum()) {
+ print_const_def_value(out, name, type, value);
+ }
+
+ return need_static_construction;
+}
+
+std::string t_csharp_generator::render_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << value->get_integer();
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << type->get_name() << "." << value->get_identifier_name();
+ } else {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true, true, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+void t_csharp_generator::generate_struct(t_struct* tstruct) {
+ if (union_ && tstruct->is_union()) {
+ generate_csharp_union(tstruct);
+ } else {
+ generate_csharp_struct(tstruct, false);
+ }
+}
+
+void t_csharp_generator::generate_xception(t_struct* txception) {
+ generate_csharp_struct(txception, true);
+}
+
+void t_csharp_generator::generate_csharp_struct(t_struct* tstruct, bool is_exception) {
+ string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_struct;
+
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl;
+
+ generate_csharp_struct_definition(f_struct, tstruct, is_exception);
+
+ f_struct.close();
+}
+
+void t_csharp_generator::generate_csharp_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool in_class,
+ bool is_result) {
+
+ if (!in_class) {
+ start_csharp_namespace(out);
+ }
+
+ out << endl;
+
+ generate_csharp_doc(out, tstruct);
+ prepare_member_name_mapping(tstruct);
+
+ indent(out) << "#if !SILVERLIGHT" << endl;
+ indent(out) << "[Serializable]" << endl;
+ indent(out) << "#endif" << endl;
+ if ((serialize_ || wcf_) && !is_exception) {
+ indent(out) << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]"
+ << endl; // do not make exception classes directly WCF serializable, we provide a
+ // separate "fault" for that
+ }
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+
+ indent(out) << "public " << (is_final ? "sealed " : "") << "partial class "
+ << normalize_name(tstruct->get_name()) << " : ";
+
+ if (is_exception) {
+ out << "TException, ";
+ }
+ out << "TBase";
+
+ out << endl;
+
+ scope_up(out);
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // make private members with public Properties
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ // if the field is requied, then we use auto-properties
+ if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter)))) {
+ indent(out) << "private " << declare_field(*m_iter, false, "_") << endl;
+ }
+ }
+ out << endl;
+
+ bool has_non_required_fields = false;
+ bool has_non_required_default_value_fields = false;
+ bool has_required_fields = false;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_csharp_doc(out, *m_iter);
+ generate_property(out, *m_iter, true, true);
+ bool is_required = field_is_required((*m_iter));
+ bool has_default = field_has_default((*m_iter));
+ if (is_required) {
+ has_required_fields = true;
+ } else {
+ if (has_default) {
+ has_non_required_default_value_fields = true;
+ }
+ has_non_required_fields = true;
+ }
+ }
+
+ bool generate_isset = (nullable_ && has_non_required_default_value_fields)
+ || (!nullable_ && has_non_required_fields);
+ if (generate_isset) {
+ out << endl;
+ if (serialize_ || wcf_) {
+ out << indent() << "[XmlIgnore] // XmlSerializer" << endl << indent()
+ << "[DataMember(Order = 1)] // XmlObjectSerializer, DataContractJsonSerializer, etc."
+ << endl;
+ }
+ out << indent() << "public Isset __isset;" << endl << indent() << "#if !SILVERLIGHT" << endl
+ << indent() << "[Serializable]" << endl << indent() << "#endif" << endl;
+ if (serialize_ || wcf_) {
+ indent(out) << "[DataContract]" << endl;
+ }
+ indent(out) << "public struct Isset {" << endl;
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ bool is_required = field_is_required((*m_iter));
+ bool has_default = field_has_default((*m_iter));
+ // if it is required, don't need Isset for that variable
+ // if it is not required, if it has a default value, we need to generate Isset
+ // if we are not nullable, then we generate Isset
+ if (!is_required && (!nullable_ || has_default)) {
+ if (serialize_ || wcf_) {
+ indent(out) << "[DataMember]" << endl;
+ }
+ indent(out) << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ if (generate_isset && (serialize_ || wcf_)) {
+ indent(out) << "#region XmlSerializer support" << endl << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ bool is_required = field_is_required((*m_iter));
+ bool has_default = field_has_default((*m_iter));
+ // if it is required, don't need Isset for that variable
+ // if it is not required, if it has a default value, we need to generate Isset
+ // if we are not nullable, then we generate Isset
+ if (!is_required && (!nullable_ || has_default)) {
+ indent(out) << "public bool ShouldSerialize" << prop_name((*m_iter)) << "()" << endl;
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+ }
+
+ indent(out) << "#endregion XmlSerializer support" << endl << endl;
+ }
+ }
+
+ // We always want a default, no argument constructor for Reading
+ indent(out) << "public " << normalize_name(tstruct->get_name()) << "() {" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef()) {
+ t = ((t_typedef*)t)->get_type();
+ }
+ if ((*m_iter)->get_value() != NULL) {
+ if (field_is_required((*m_iter))) {
+ print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true);
+ } else {
+ print_const_value(out,
+ "this._" + (*m_iter)->get_name(),
+ t,
+ (*m_iter)->get_value(),
+ true,
+ true);
+ // Optionals with defaults are marked set
+ indent(out) << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;"
+ << endl;
+ }
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ if (has_required_fields) {
+ indent(out) << "public " << tstruct->get_name() << "(";
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (field_is_required((*m_iter))) {
+ if (first) {
+ first = false;
+ } else {
+ out << ", ";
+ }
+ out << type_name((*m_iter)->get_type()) << " " << normalize_name((*m_iter)->get_name());
+ }
+ }
+ out << ") : this() {" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (field_is_required((*m_iter))) {
+ indent(out) << "this." << prop_name((*m_iter)) << " = " << normalize_name((*m_iter)->get_name()) << ";"
+ << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ generate_csharp_struct_reader(out, tstruct);
+ if (is_result) {
+ generate_csharp_struct_result_writer(out, tstruct);
+ } else {
+ generate_csharp_struct_writer(out, tstruct);
+ }
+ if (hashcode_) {
+ generate_csharp_struct_equals(out, tstruct);
+ generate_csharp_struct_hashcode(out, tstruct);
+ }
+ generate_csharp_struct_tostring(out, tstruct);
+ scope_down(out);
+ out << endl;
+
+ // generate a corresponding WCF fault to wrap the exception
+ if ((serialize_ || wcf_) && is_exception) {
+ generate_csharp_wcffault(out, tstruct);
+ }
+
+ cleanup_member_name_mapping(tstruct);
+ if (!in_class) {
+ end_csharp_namespace(out);
+ }
+}
+
+void t_csharp_generator::generate_csharp_wcffault(ostream& out, t_struct* tstruct) {
+ out << endl;
+ indent(out) << "#if !SILVERLIGHT" << endl;
+ indent(out) << "[Serializable]" << endl;
+ indent(out) << "#endif" << endl;
+ indent(out) << "[DataContract]" << endl;
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+
+ indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name()
+ << "Fault" << endl;
+
+ scope_up(out);
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // make private members with public Properties
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ // if the field is requied, then we use auto-properties
+ if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter)))) {
+ indent(out) << "private " << declare_field(*m_iter, false, "_") << endl;
+ }
+ }
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_property(out, *m_iter, true, false);
+ }
+
+ scope_down(out);
+ out << endl;
+}
+
+void t_csharp_generator::generate_csharp_struct_reader(ostream& out, t_struct* tstruct) {
+ indent(out) << "public void Read (TProtocol iprot)" << endl;
+ scope_up(out);
+
+ out << indent() << "iprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Required variables aren't in __isset, so we need tmp vars to check them
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (field_is_required((*f_iter))) {
+ indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
+ }
+ }
+
+ indent(out) << "TField field;" << endl << indent() << "iprot.ReadStructBegin();" << endl;
+
+ indent(out) << "while (true)" << endl;
+ scope_up(out);
+
+ indent(out) << "field = iprot.ReadFieldBegin();" << endl;
+
+ indent(out) << "if (field.Type == TType.Stop) { " << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent(out) << "switch (field.ID)" << endl;
+
+ scope_up(out);
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool is_required = field_is_required((*f_iter));
+ indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter);
+ if (is_required) {
+ indent(out) << "isset_" << (*f_iter)->get_name() << " = true;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);"
+ << endl << indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default: " << endl;
+ indent_up();
+ indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl;
+ indent(out) << "break;" << endl;
+ indent_down();
+
+ scope_down(out);
+
+ indent(out) << "iprot.ReadFieldEnd();" << endl;
+
+ scope_down(out);
+
+ indent(out) << "iprot.ReadStructEnd();" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (field_is_required((*f_iter))) {
+ indent(out) << "if (!isset_" << (*f_iter)->get_name() << ")" << endl;
+ indent_up();
+ out << indent()
+ << "throw new TProtocolException(TProtocolException.INVALID_DATA, "
+ << "\"required field " << prop_name((*f_iter)) << " not set\");"
+ << endl;
+ indent_down();
+ }
+ }
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "iprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_csharp_struct_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public void Write(TProtocol oprot) {" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl;
+ indent(out) << "oprot.WriteStructBegin(struc);" << endl;
+
+ if (fields.size() > 0) {
+ indent(out) << "TField field = new TField();" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool is_required = field_is_required((*f_iter));
+ bool has_default = field_has_default((*f_iter));
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+
+ if (is_required)
+ {
+ if (null_allowed) {
+ indent(out) << "if (" << prop_name((*f_iter)) << " == null)" << endl;
+ indent_up();
+ out << indent()
+ << "throw new TProtocolException(TProtocolException.INVALID_DATA, "
+ << "\"required field " << prop_name((*f_iter)) << " not set\");"
+ << endl;
+ indent_down();
+ }
+ }
+ else
+ {
+ if (nullable_ && !has_default) {
+ indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl;
+ }
+ else if (null_allowed) {
+ out << indent()
+ << "if (" << prop_name((*f_iter)) << " != null && __isset."
+ << normalize_name((*f_iter)->get_name()) << ") {"
+ << endl;
+ }
+ else {
+ indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl;
+ }
+ indent_up();
+ }
+ indent(out) << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl;
+ indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl;
+ indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl;
+ indent(out) << "oprot.WriteFieldBegin(field);" << endl;
+
+ generate_serialize_field(out, *f_iter);
+
+ indent(out) << "oprot.WriteFieldEnd();" << endl;
+ if (!is_required) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+ }
+
+ indent(out) << "oprot.WriteFieldStop();" << endl;
+ indent(out) << "oprot.WriteStructEnd();" << endl;
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_csharp_struct_result_writer(ostream& out, t_struct* tstruct) {
+ indent(out) << "public void Write(TProtocol oprot) {" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "TStruct struc = new TStruct(\"" << name << "\");" << endl;
+ indent(out) << "oprot.WriteStructBegin(struc);" << endl;
+
+ if (fields.size() > 0) {
+ indent(out) << "TField field = new TField();" << endl;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ out << endl << indent() << "if ";
+ } else {
+ out << " else if ";
+ }
+
+ if (nullable_) {
+ out << "(this." << prop_name((*f_iter)) << " != null) {" << endl;
+ } else {
+ out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl;
+ }
+ indent_up();
+
+ bool null_allowed = !nullable_ && type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ indent(out) << "if (" << prop_name(*f_iter) << " != null) {" << endl;
+ indent_up();
+ }
+
+ indent(out) << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl;
+ indent(out) << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl;
+ indent(out) << "field.ID = " << (*f_iter)->get_key() << ";" << endl;
+ indent(out) << "oprot.WriteFieldBegin(field);" << endl;
+
+ generate_serialize_field(out, *f_iter);
+
+ indent(out) << "oprot.WriteFieldEnd();" << endl;
+
+ if (null_allowed) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}";
+ }
+ }
+
+ out << endl << indent() << "oprot.WriteFieldStop();" << endl << indent()
+ << "oprot.WriteStructEnd();" << endl;
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_csharp_struct_tostring(ostream& out, t_struct* tstruct) {
+ indent(out) << "public override string ToString() {" << endl;
+ indent_up();
+
+ indent(out) << "StringBuilder __sb = new StringBuilder(\"" << tstruct->get_name() << "(\");"
+ << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool useFirstFlag = false;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (!field_is_required((*f_iter))) {
+ indent(out) << "bool __first = true;" << endl;
+ useFirstFlag = true;
+ }
+ break;
+ }
+
+ bool had_required = false; // set to true after first required field has been processed
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool is_required = field_is_required((*f_iter));
+ bool has_default = field_has_default((*f_iter));
+ if (nullable_ && !has_default && !is_required) {
+ indent(out) << "if (" << prop_name((*f_iter)) << " != null) {" << endl;
+ indent_up();
+ } else if (!is_required) {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ indent(out) << "if (" << prop_name((*f_iter)) << " != null && __isset."
+ << normalize_name((*f_iter)->get_name()) << ") {" << endl;
+ indent_up();
+ } else {
+ indent(out) << "if (__isset." << normalize_name((*f_iter)->get_name()) << ") {" << endl;
+ indent_up();
+ }
+ }
+
+ if (useFirstFlag && (!had_required)) {
+ indent(out) << "if(!__first) { __sb.Append(\", \"); }" << endl;
+ if (!is_required) {
+ indent(out) << "__first = false;" << endl;
+ }
+ indent(out) << "__sb.Append(\"" << prop_name((*f_iter)) << ": \");" << endl;
+ } else {
+ indent(out) << "__sb.Append(\", " << prop_name((*f_iter)) << ": \");" << endl;
+ }
+
+ t_type* ttype = (*f_iter)->get_type();
+ if (ttype->is_xception() || ttype->is_struct()) {
+ indent(out) << "__sb.Append(" << prop_name((*f_iter))
+ << "== null ? \"<null>\" : " << prop_name((*f_iter)) << ".ToString());" << endl;
+ } else {
+ indent(out) << "__sb.Append(" << prop_name((*f_iter)) << ");" << endl;
+ }
+
+ if (!is_required) {
+ indent_down();
+ indent(out) << "}" << endl;
+ } else {
+ had_required = true; // now __first must be false, so we don't need to check it anymore
+ }
+ }
+
+ indent(out) << "__sb.Append(\")\");" << endl;
+ indent(out) << "return __sb.ToString();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_csharp_union(t_struct* tunion) {
+ string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_union;
+
+ f_union.open(f_union_name.c_str());
+
+ f_union << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl;
+
+ generate_csharp_union_definition(f_union, tunion);
+
+ f_union.close();
+}
+
+void t_csharp_generator::generate_csharp_union_definition(std::ostream& out, t_struct* tunion) {
+ // Let's define the class first
+ start_csharp_namespace(out);
+
+ indent(out) << "public abstract partial class " << tunion->get_name() << " : TAbstractBase {"
+ << endl;
+
+ indent_up();
+
+ indent(out) << "public abstract void Write(TProtocol protocol);" << endl;
+ indent(out) << "public readonly int Isset;" << endl;
+ indent(out) << "public abstract object Data { get; }" << endl;
+
+ indent(out) << "protected " << tunion->get_name() << "(int isset) {" << endl;
+ indent_up();
+ indent(out) << "Isset = isset;" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public class ___undefined : " << tunion->get_name() << " {" << endl;
+ indent_up();
+
+ indent(out) << "public override object Data { get { return null; } }" << endl;
+
+ indent(out) << "public ___undefined() : base(0) {}" << endl << endl;
+
+ indent(out) << "public override void Write(TProtocol protocol) {" << endl;
+ indent_up();
+ indent(out) << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist "
+ "an union type which is not set.\");" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ const vector<t_field*>& fields = tunion->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ generate_csharp_union_class(out, tunion, (*f_iter));
+ }
+
+ generate_csharp_union_reader(out, tunion);
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ end_csharp_namespace(out);
+}
+
+void t_csharp_generator::generate_csharp_union_class(std::ostream& out,
+ t_struct* tunion,
+ t_field* tfield) {
+ indent(out) << "public " << type_name(tfield->get_type()) << " As_" << tfield->get_name() << endl;
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "get" << endl;
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "return (" << tfield->get_key() << " == Isset) ? (" << type_name(tfield->get_type()) << ")Data : default(" << type_name(tfield->get_type()) << ");" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl
+ << endl;
+
+
+ indent(out) << "public class " << tfield->get_name() << " : " << tunion->get_name() << " {"
+ << endl;
+ indent_up();
+ indent(out) << "private " << type_name(tfield->get_type()) << " _data;" << endl;
+ indent(out) << "public override object Data { get { return _data; } }" << endl;
+ indent(out) << "public " << tfield->get_name() << "(" << type_name(tfield->get_type())
+ << " data) : base("<< tfield->get_key() <<") {" << endl;
+ indent_up();
+ indent(out) << "this._data = data;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent(out) << "public override void Write(TProtocol oprot) {" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ indent(out) << "TStruct struc = new TStruct(\"" << tunion->get_name() << "\");" << endl;
+ indent(out) << "oprot.WriteStructBegin(struc);" << endl;
+
+ indent(out) << "TField field = new TField();" << endl;
+ indent(out) << "field.Name = \"" << tfield->get_name() << "\";" << endl;
+ indent(out) << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl;
+ indent(out) << "field.ID = " << tfield->get_key() << ";" << endl;
+ indent(out) << "oprot.WriteFieldBegin(field);" << endl;
+
+ generate_serialize_field(out, tfield, "_data", true, true);
+
+ indent(out) << "oprot.WriteFieldEnd();" << endl;
+ indent(out) << "oprot.WriteFieldStop();" << endl;
+ indent(out) << "oprot.WriteStructEnd();" << endl;
+ indent_down();
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_csharp_struct_equals(ostream& out, t_struct* tstruct) {
+ indent(out) << "public override bool Equals(object that) {" << endl;
+ indent_up();
+
+ indent(out) << "var other = that as " << type_name(tstruct) << ";" << endl;
+ indent(out) << "if (other == null) return false;" << endl;
+ indent(out) << "if (ReferenceEquals(this, other)) return true;" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool first = true;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ indent(out) << "return ";
+ indent_up();
+ } else {
+ out << endl;
+ indent(out) << "&& ";
+ }
+ if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) {
+ out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset."
+ << normalize_name((*f_iter)->get_name()) << ") && ((!__isset."
+ << normalize_name((*f_iter)->get_name()) << ") || (";
+ }
+ t_type* ttype = (*f_iter)->get_type();
+ if (ttype->is_container() || ttype->is_binary()) {
+ out << "TCollections.Equals(";
+ } else {
+ out << "System.Object.Equals(";
+ }
+ out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")";
+ if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) {
+ out << ")))";
+ }
+ }
+ if (first) {
+ indent(out) << "return true;" << endl;
+ } else {
+ out << ";" << endl;
+ indent_down();
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_csharp_struct_hashcode(ostream& out, t_struct* tstruct) {
+ indent(out) << "public override int GetHashCode() {" << endl;
+ indent_up();
+
+ indent(out) << "int hashcode = 0;" << endl;
+ indent(out) << "unchecked {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_type* ttype = (*f_iter)->get_type();
+ indent(out) << "hashcode = (hashcode * 397) ^ ";
+ if (field_is_required((*f_iter))) {
+ out << "(";
+ } else if (nullable_) {
+ out << "(" << prop_name((*f_iter)) << " == null ? 0 : ";
+ } else {
+ out << "(!__isset." << normalize_name((*f_iter)->get_name()) << " ? 0 : ";
+ }
+ if (ttype->is_container()) {
+ out << "(TCollections.GetHashCode(" << prop_name((*f_iter)) << "))";
+ } else {
+ out << "(" << prop_name((*f_iter)) << ".GetHashCode())";
+ }
+ out << ");" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl;
+ indent(out) << "return hashcode;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_service(t_service* tservice) {
+ string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << csharp_type_usings() << csharp_thrift_usings() << endl;
+
+ start_csharp_namespace(f_service_);
+
+ indent(f_service_) << "public partial class " << normalize_name(service_name_) << " {" << endl;
+ indent_up();
+
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+
+ indent_down();
+
+ indent(f_service_) << "}" << endl;
+ end_csharp_namespace(f_service_);
+ f_service_.close();
+}
+
+void t_csharp_generator::generate_service_interface(t_service* tservice) {
+ generate_separate_service_interfaces(tservice);
+}
+
+void t_csharp_generator::generate_separate_service_interfaces(t_service* tservice) {
+ generate_sync_service_interface(tservice);
+
+ if (async_) {
+ generate_async_service_interface(tservice);
+ }
+
+ generate_combined_service_interface(tservice);
+}
+
+void t_csharp_generator::generate_sync_service_interface(t_service* tservice) {
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " : " + extends + ".ISync";
+ }
+
+ generate_csharp_doc(f_service_, tservice);
+
+ if (wcf_) {
+ indent(f_service_) << "[System.ServiceModel.ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+ indent(f_service_) << "public interface ISync" << extends_iface << " {" << endl;
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_csharp_doc(f_service_, *f_iter);
+
+ // if we're using WCF, add the corresponding attributes
+ if (wcf_) {
+ indent(f_service_) << "[System.ServiceModel.OperationContract]" << endl;
+
+ const std::vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent(f_service_) << "[System.ServiceModel.FaultContract(typeof("
+ + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl;
+ }
+ }
+
+ indent(f_service_) << function_signature(*f_iter) << ";" << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_async_service_interface(t_service* tservice) {
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " : " + extends + ".IAsync";
+ }
+
+ generate_csharp_doc(f_service_, tservice);
+
+ if (wcf_) {
+ indent(f_service_) << "[System.ServiceModel.ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+ indent(f_service_) << "public interface IAsync" << extends_iface << " {" << endl;
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_csharp_doc(f_service_, *f_iter);
+
+ // if we're using WCF, add the corresponding attributes
+ if (wcf_) {
+ indent(f_service_) << "[System.ServiceModel.OperationContract]" << endl;
+
+ const std::vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent(f_service_) << "[System.ServiceModel.FaultContract(typeof("
+ + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl;
+ }
+ }
+
+ indent(f_service_) << function_signature_async(*f_iter) << ";" << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_combined_service_interface(t_service* tservice) {
+ string extends_iface = " : ISync";
+
+ if (async_) {
+ extends_iface += ", IAsync";
+ }
+
+ generate_csharp_doc(f_service_, tservice);
+
+ if (wcf_) {
+ indent(f_service_) << "[System.ServiceModel.ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+
+ indent(f_service_) << "public interface Iface" << extends_iface << " {" << endl;
+
+ indent_up();
+
+ // We need to generate extra old style async methods for silverlight. Since
+ // this isn't something you'd want to implement server-side, just put them into
+ // the main Iface interface.
+ generate_silverlight_async_methods(tservice);
+
+ indent_down();
+
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_silverlight_async_methods(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_csharp_doc(f_service_, *f_iter);
+
+ // For backwards compatibility, include the Begin_, End_ methods if we're generating
+ // with the async flag. I'm not sure this is necessary, so someone with more knowledge
+ // can maybe remove these checks if they know it's safe.
+ if (!async_) {
+ indent(f_service_) << "#if SILVERLIGHT" << endl;
+ }
+
+ indent(f_service_) << function_signature_async_begin(*f_iter, "Begin_") << ";" << endl;
+ indent(f_service_) << function_signature_async_end(*f_iter, "End_") << ";" << endl;
+
+ if (!async_) {
+ indent(f_service_) << "#endif" << endl;
+ }
+ }
+}
+
+void t_csharp_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_csharp_struct_definition(f_service_, ts, false, true);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+void t_csharp_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_client = extends + ".Client, ";
+ } else {
+ extends_client = "IDisposable, ";
+ }
+
+ generate_csharp_doc(f_service_, tservice);
+
+ indent(f_service_) << "public class Client : " << extends_client << "Iface {" << endl;
+ indent_up();
+ indent(f_service_) << "public Client(TProtocol prot) : this(prot, prot)" << endl;
+ scope_up(f_service_);
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << "public Client(TProtocol iprot, TProtocol oprot)";
+ if (!extends.empty()) {
+ f_service_ << " : base(iprot, oprot)";
+ }
+ f_service_ << endl;
+
+ scope_up(f_service_);
+ if (extends.empty()) {
+ f_service_ << indent() << "iprot_ = iprot;" << endl << indent() << "oprot_ = oprot;" << endl;
+ }
+ scope_down(f_service_);
+
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected TProtocol iprot_;" << endl << indent()
+ << "protected TProtocol oprot_;" << endl << indent() << "protected int seqid_;"
+ << endl << endl;
+
+ f_service_ << indent() << "public TProtocol InputProtocol" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "get { return iprot_; }" << endl;
+ scope_down(f_service_);
+
+ f_service_ << indent() << "public TProtocol OutputProtocol" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "get { return oprot_; }" << endl;
+ scope_down(f_service_);
+ f_service_ << endl << endl;
+
+ indent(f_service_) << "#region \" IDisposable Support \"" << endl;
+ indent(f_service_) << "private bool _IsDisposed;" << endl << endl;
+ indent(f_service_) << "// IDisposable" << endl;
+ indent(f_service_) << "public void Dispose()" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "Dispose(true);" << endl;
+ scope_down(f_service_);
+ indent(f_service_) << endl << endl;
+ indent(f_service_) << "protected virtual void Dispose(bool disposing)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "if (!_IsDisposed)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "if (disposing)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "if (iprot_ != null)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "((IDisposable)iprot_).Dispose();" << endl;
+ scope_down(f_service_);
+ indent(f_service_) << "if (oprot_ != null)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "((IDisposable)oprot_).Dispose();" << endl;
+ scope_down(f_service_);
+ scope_down(f_service_);
+ scope_down(f_service_);
+ indent(f_service_) << "_IsDisposed = true;" << endl;
+ scope_down(f_service_);
+ indent(f_service_) << "#endregion" << endl;
+ f_service_ << endl << endl;
+ }
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+
+ indent(f_service_) << endl;
+
+ if (!async_) {
+ indent(f_service_) << "#if SILVERLIGHT" << endl;
+ indent(f_service_) << endl;
+ }
+ // Begin_
+ indent(f_service_) << "public " << function_signature_async_begin(*f_iter, "Begin_") << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return "
+ << "send_" << funname << "(callback, state";
+
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ prepare_member_name_mapping(arg_struct);
+
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << ", ";
+ f_service_ << normalize_name((*fld_iter)->get_name());
+ }
+ f_service_ << ");" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // End
+ indent(f_service_) << "public " << function_signature_async_end(*f_iter, "End_") << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "oprot_.Transport.EndFlush(asyncResult);" << endl;
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "recv_" << funname << "();" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // async
+ bool first;
+ if (async_) {
+ indent(f_service_) << "public async " << function_signature_async(*f_iter, "") << endl;
+ scope_up(f_service_);
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << type_name((*f_iter)->get_returntype()) << " retval;" << endl;
+ indent(f_service_) << "retval = ";
+ } else {
+ indent(f_service_);
+ }
+ f_service_ << "await Task.Run(() =>" << endl;
+ scope_up(f_service_);
+ indent(f_service_);
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << funname << "(";
+ first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << (*fld_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+ indent_down();
+ indent(f_service_) << "});" << endl;
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "return retval;" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ if (!async_) {
+ indent(f_service_) << "#endif" << endl << endl;
+ }
+
+ generate_csharp_doc(f_service_, *f_iter);
+ indent(f_service_) << "public " << function_signature(*f_iter) << endl;
+ scope_up(f_service_);
+
+ // silverlight invoke
+ if (!async_) {
+ indent(f_service_) << "#if SILVERLIGHT" << endl;
+
+ indent(f_service_) << "var asyncResult = Begin_" << funname << "(null, null";
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << ", " << normalize_name((*fld_iter)->get_name());
+ }
+ f_service_ << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "End_" << funname << "(asyncResult);" << endl;
+ }
+ f_service_ << endl;
+
+ indent(f_service_) << "#else" << endl;
+ }
+
+ // synchronous invoke
+ indent(f_service_) << "send_" << funname << "(";
+
+ first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << normalize_name((*fld_iter)->get_name());
+ }
+ f_service_ << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "recv_" << funname << "();" << endl;
+ }
+ f_service_ << endl;
+
+ if (!async_) {
+ indent(f_service_) << "#endif" << endl;
+ }
+ scope_down(f_service_);
+
+ // Send
+ t_function send_function(g_type_void,
+ string("send_") + (*f_iter)->get_name(),
+ (*f_iter)->get_arglist());
+
+ string argsname = (*f_iter)->get_name() + "_args";
+
+ if (!async_) {
+ indent(f_service_) << "#if SILVERLIGHT" << endl;
+ }
+
+ indent(f_service_) << "public " << function_signature_async_begin(&send_function) << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", "
+ << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call")
+ << ", seqid_));" << endl << indent() << argsname << " args = new " << argsname
+ << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args." << prop_name(*fld_iter) << " = "
+ << normalize_name((*fld_iter)->get_name()) << ";" << endl;
+ }
+
+ f_service_ << indent() << "args.Write(oprot_);" << endl << indent()
+ << "oprot_.WriteMessageEnd();" << endl;
+ indent(f_service_) << "return oprot_.Transport.BeginFlush(callback, state);" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (!async_) {
+ indent(f_service_) << "#else" << endl;
+ f_service_ << endl;
+ }
+
+ indent(f_service_) << "public " << function_signature(&send_function) << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", "
+ << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call")
+ << ", seqid_));" << endl << indent() << argsname << " args = new " << argsname
+ << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args." << prop_name(*fld_iter) << " = "
+ << normalize_name((*fld_iter)->get_name()) << ";" << endl;
+ }
+
+ f_service_ << indent() << "args.Write(oprot_);" << endl << indent()
+ << "oprot_.WriteMessageEnd();" << endl;
+
+ indent(f_service_) << "oprot_.Transport.Flush();" << endl;
+ cleanup_member_name_mapping(arg_struct);
+ scope_down(f_service_);
+
+ if (!async_) {
+ indent(f_service_) << "#endif" << endl;
+ }
+
+ f_service_ << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ string resultname = (*f_iter)->get_name() + "_result";
+
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs,
+ (*f_iter)->get_xceptions());
+ indent(f_service_) << "public " << function_signature(&recv_function) << endl;
+ scope_up(f_service_);
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+
+ f_service_ << indent() << "TMessage msg = iprot_.ReadMessageBegin();" << endl << indent()
+ << "if (msg.Type == TMessageType.Exception) {" << endl;
+ indent_up();
+ f_service_ << indent() << "TApplicationException x = TApplicationException.Read(iprot_);"
+ << endl << indent() << "iprot_.ReadMessageEnd();" << endl << indent() << "throw x;"
+ << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl << indent() << resultname << " result = new "
+ << resultname << "();" << endl << indent() << "result.Read(iprot_);" << endl
+ << indent() << "iprot_.ReadMessageEnd();" << endl;
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ if (nullable_) {
+ if (type_can_be_null((*f_iter)->get_returntype())) {
+ f_service_ << indent() << "if (result.Success != null) {" << endl << indent()
+ << " return result.Success;" << endl << indent() << "}" << endl;
+ } else {
+ f_service_ << indent() << "if (result.Success.HasValue) {" << endl << indent()
+ << " return result.Success.Value;" << endl << indent() << "}" << endl;
+ }
+ } else {
+ f_service_ << indent() << "if (result.__isset.success) {" << endl << indent()
+ << " return result.Success;" << endl << indent() << "}" << endl;
+ }
+ }
+
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ if (nullable_) {
+ f_service_ << indent() << "if (result." << prop_name(*x_iter) << " != null) {" << endl
+ << indent() << " throw result." << prop_name(*x_iter) << ";" << endl
+ << indent() << "}" << endl;
+ } else {
+ f_service_ << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name())
+ << ") {" << endl << indent() << " throw result." << prop_name(*x_iter) << ";"
+ << endl << indent() << "}" << endl;
+ }
+ }
+
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "return;" << endl;
+ } else {
+ f_service_ << indent()
+ << "throw new "
+ "TApplicationException(TApplicationException.ExceptionType.MissingResult, \""
+ << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
+ }
+
+ cleanup_member_name_mapping((*f_iter)->get_xceptions());
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl;
+}
+
+void t_csharp_generator::generate_service_server(t_service* tservice) {
+ if (async_) {
+ generate_service_server_async(tservice);
+ generate_service_server_sync(tservice);
+ }
+ else {
+ generate_service_server_sync(tservice);
+ }
+}
+
+void t_csharp_generator::generate_service_server_sync(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".Processor, ";
+ }
+
+ indent(f_service_) << "public class Processor : " << extends_processor << "TProcessor {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public Processor(ISync iface)";
+
+ if (!extends.empty()) {
+ f_service_ << " : base(iface)";
+ }
+ f_service_ << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "iface_ = iface;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "processMap_[\"" << (*f_iter)->get_name()
+ << "\"] = " << (*f_iter)->get_name() << "_Process;" << endl;
+ }
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_
+ << indent()
+ << "protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);"
+ << endl;
+ }
+
+ f_service_ << indent() << "private ISync iface_;" << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new "
+ "Dictionary<string, ProcessFunction>();" << endl;
+ }
+
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ indent(f_service_) << "public bool Process(TProtocol iprot, TProtocol oprot)" << endl;
+ }
+ else {
+ indent(f_service_) << "public new bool Process(TProtocol iprot, TProtocol oprot)" << endl;
+ }
+ scope_up(f_service_);
+
+ f_service_ << indent() << "try" << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl;
+
+ f_service_
+ << indent() << "ProcessFunction fn;" << endl << indent()
+ << "processMap_.TryGetValue(msg.Name, out fn);" << endl << indent() << "if (fn == null) {"
+ << endl << indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << indent()
+ << " iprot.ReadMessageEnd();" << endl << indent()
+ << " TApplicationException x = new TApplicationException "
+ "(TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + "
+ "msg.Name + \"'\");" << endl << indent()
+ << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));"
+ << endl << indent() << " x.Write(oprot);" << endl << indent() << " oprot.WriteMessageEnd();"
+ << endl << indent() << " oprot.Transport.Flush();" << endl << indent() << " return true;"
+ << endl << indent() << "}" << endl << indent() << "fn(msg.SeqID, iprot, oprot);" << endl;
+
+ scope_down(f_service_);
+
+ f_service_ << indent() << "catch (IOException)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "return false;" << endl;
+ scope_down(f_service_);
+
+ f_service_ << indent() << "return true;" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_service_server_async(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".AsyncProcessor, ";
+ }
+
+ indent(f_service_) << "public class AsyncProcessor : " << extends_processor << "TAsyncProcessor {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public AsyncProcessor(IAsync iface)";
+ if (!extends.empty()) {
+ f_service_ << " : base(iface)";
+ }
+ f_service_ << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "iface_ = iface;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "processMap_[\"" << (*f_iter)->get_name()
+ << "\"] = " << (*f_iter)->get_name() << "_ProcessAsync;" << endl;
+ }
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_
+ << indent()
+ << "protected delegate Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);"
+ << endl;
+ }
+
+ f_service_ << indent() << "private IAsync iface_;" << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new "
+ "Dictionary<string, ProcessFunction>();" << endl;
+ }
+
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ indent(f_service_) << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl;
+ }
+ else {
+ indent(f_service_) << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl;
+ }
+ scope_up(f_service_);
+
+ f_service_ << indent() << "try" << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl;
+
+ f_service_
+ << indent() << "ProcessFunction fn;" << endl << indent()
+ << "processMap_.TryGetValue(msg.Name, out fn);" << endl << indent() << "if (fn == null) {"
+ << endl << indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << indent()
+ << " iprot.ReadMessageEnd();" << endl << indent()
+ << " TApplicationException x = new TApplicationException "
+ "(TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + "
+ "msg.Name + \"'\");" << endl << indent()
+ << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));"
+ << endl << indent() << " x.Write(oprot);" << endl << indent() << " oprot.WriteMessageEnd();"
+ << endl << indent() << " oprot.Transport.Flush();" << endl << indent() << " return true;"
+ << endl << indent() << "}" << endl << indent() << "await fn(msg.SeqID, iprot, oprot);" << endl;
+
+ scope_down(f_service_);
+
+ f_service_ << indent() << "catch (IOException)" << endl;
+ scope_up(f_service_);
+ f_service_ << indent() << "return false;" << endl;
+ scope_down(f_service_);
+
+ f_service_ << indent() << "return true;" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function_async(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_csharp_struct_definition(f_service_, &result, false, true, true);
+}
+
+void t_csharp_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ indent(f_service_) << "public void " << tfunction->get_name()
+ << "_Process(int seqid, TProtocol iprot, TProtocol oprot)" << endl;
+ scope_up(f_service_);
+
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+
+ f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl
+ << indent() << "args.Read(iprot);" << endl
+ << indent() << "iprot.ReadMessageEnd();" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl;
+ }
+
+ f_service_ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.Success = ";
+ }
+ f_service_ << "iface_." << normalize_name(tfunction->get_name()) << "(";
+ bool first = true;
+ prepare_member_name_mapping(arg_struct);
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << prop_name(*f_iter);
+ if (nullable_ && !type_can_be_null((*f_iter)->get_type())) {
+ f_service_ << ".Value";
+ }
+ }
+ cleanup_member_name_mapping(arg_struct);
+ f_service_ << ");" << endl;
+
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+ if (xceptions.size() > 0) {
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "catch (" << type_name((*x_iter)->get_type(), false, false) << " "
+ << (*x_iter)->get_name() << ")" << endl
+ << indent() << "{" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ f_service_ << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name()
+ << ";" << endl;
+ indent_down();
+ }
+ f_service_ << indent() << "}" << endl;
+ }
+ }
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.Reply, seqid)); " << endl;
+ f_service_ << indent() << "result.Write(oprot);" << endl;
+ }
+ indent_down();
+
+ cleanup_member_name_mapping(xs);
+
+ f_service_ << indent() << "}" << endl
+ << indent() << "catch (TTransportException)" << endl
+ << indent() << "{" << endl
+ << indent() << " throw;" << endl
+ << indent() << "}" << endl
+ << indent() << "catch (Exception ex)" << endl
+ << indent() << "{" << endl
+ << indent() << " Console.Error.WriteLine(\"Error occurred in processor:\");" << endl
+ << indent() << " Console.Error.WriteLine(ex.ToString());" << endl;
+
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "}" << endl;
+ } else {
+ f_service_ << indent() << " TApplicationException x = new TApplicationException" << indent()
+ << "(TApplicationException.ExceptionType.InternalError,\" Internal error.\");"
+ << endl
+ << indent() << " oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.Exception, seqid));" << endl
+ << indent() << " x.Write(oprot);" << endl
+ << indent() << "}" << endl;
+ f_service_ << indent() << "oprot.WriteMessageEnd();" << endl
+ << indent() << "oprot.Transport.Flush();" << endl;
+ }
+
+ scope_down(f_service_);
+
+ f_service_ << endl;
+}
+
+void t_csharp_generator::generate_process_function_async(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ indent(f_service_) << "public async Task " << tfunction->get_name()
+ << "_ProcessAsync(int seqid, TProtocol iprot, TProtocol oprot)" << endl;
+ scope_up(f_service_);
+
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+
+ f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl
+ << indent() << "args.Read(iprot);" << endl
+ << indent() << "iprot.ReadMessageEnd();" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl;
+ }
+
+ f_service_ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.Success = ";
+ }
+ f_service_ << "await iface_." << normalize_name(tfunction->get_name()) << "Async(";
+ bool first = true;
+ prepare_member_name_mapping(arg_struct);
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ }
+ else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << prop_name(*f_iter);
+ if (nullable_ && !type_can_be_null((*f_iter)->get_type())) {
+ f_service_ << ".Value";
+ }
+ }
+ cleanup_member_name_mapping(arg_struct);
+ f_service_ << ");" << endl;
+
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+ if (xceptions.size() > 0) {
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "catch (" << type_name((*x_iter)->get_type(), false, false) << " "
+ << (*x_iter)->get_name() << ")" << endl
+ << indent() << "{" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ f_service_ << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name()
+ << ";" << endl;
+ indent_down();
+ }
+ f_service_ << indent() << "}" << endl;
+ }
+ }
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.Reply, seqid)); " << endl;
+ f_service_ << indent() << "result.Write(oprot);" << endl;
+ }
+ indent_down();
+
+ cleanup_member_name_mapping(xs);
+
+ f_service_ << indent() << "}" << endl
+ << indent() << "catch (TTransportException)" << endl
+ << indent() << "{" << endl
+ << indent() << " throw;" << endl
+ << indent() << "}" << endl
+ << indent() << "catch (Exception ex)" << endl
+ << indent() << "{" << endl
+ << indent() << " Console.Error.WriteLine(\"Error occurred in processor:\");" << endl
+ << indent() << " Console.Error.WriteLine(ex.ToString());" << endl;
+
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "}" << endl;
+ }
+ else {
+ f_service_ << indent() << " TApplicationException x = new TApplicationException" << indent()
+ << "(TApplicationException.ExceptionType.InternalError,\" Internal error.\");"
+ << endl
+ << indent() << " oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.Exception, seqid));" << endl
+ << indent() << " x.Write(oprot);" << endl
+ << indent() << "}" << endl;
+ f_service_ << indent() << "oprot.WriteMessageEnd();" << endl
+ << indent() << "oprot.Transport.Flush();" << endl;
+ }
+
+ scope_down(f_service_);
+
+ f_service_ << endl;
+}
+
+void t_csharp_generator::generate_csharp_union_reader(std::ostream& out, t_struct* tunion) {
+ // Thanks to THRIFT-1768, we don't need to check for required fields in the union
+ const vector<t_field*>& fields = tunion->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "public static " << tunion->get_name() << " Read(TProtocol iprot)" << endl;
+ scope_up(out);
+
+ out << indent() << "iprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ indent(out) << tunion->get_name() << " retval;" << endl;
+ indent(out) << "iprot.ReadStructBegin();" << endl;
+ indent(out) << "TField field = iprot.ReadFieldBegin();" << endl;
+ // we cannot have the first field be a stop -- we must have a single field defined
+ indent(out) << "if (field.Type == TType.Stop)" << endl;
+ scope_up(out);
+ indent(out) << "iprot.ReadFieldEnd();" << endl;
+ indent(out) << "retval = new ___undefined();" << endl;
+ scope_down(out);
+ indent(out) << "else" << endl;
+ scope_up(out);
+ indent(out) << "switch (field.ID)" << endl;
+ scope_up(out);
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ indent(out) << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ indent(out) << type_name((*f_iter)->get_type()) << " temp;" << endl;
+ generate_deserialize_field(out, (*f_iter), "temp", true);
+ indent(out) << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl;
+
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " TProtocolUtil.Skip(iprot, field.Type);"
+ << endl << indent() << " retval = new ___undefined();" << endl << indent() << "}" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default: " << endl;
+ indent_up();
+ indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl << indent()
+ << "retval = new ___undefined();" << endl;
+ indent(out) << "break;" << endl;
+ indent_down();
+
+ scope_down(out);
+
+ indent(out) << "iprot.ReadFieldEnd();" << endl;
+
+ indent(out) << "if (iprot.ReadFieldBegin().Type != TType.Stop)" << endl;
+ scope_up(out);
+ indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
+ scope_down(out);
+
+ // end of else for TStop
+ scope_down(out);
+ indent(out) << "iprot.ReadStructEnd();" << endl;
+ indent(out) << "return retval;" << endl;
+ indent_down();
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "iprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ indent(out) << "}" << endl << endl;
+}
+
+void t_csharp_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool is_propertyless) {
+ t_type* type = tfield->get_type();
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + (is_propertyless ? "" : prop_name(tfield));
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << name << " = ";
+
+ if (type->is_enum()) {
+ out << "(" << type_name(type, false, true) << ")";
+ }
+
+ out << "iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "ReadBinary();";
+ } else {
+ out << "ReadString();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "ReadBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "ReadByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "ReadI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "ReadI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "ReadI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "ReadDouble();";
+ break;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "ReadI32();";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+void t_csharp_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+ if (union_ && tstruct->is_union()) {
+ out << indent() << prefix << " = " << type_name(tstruct) << ".Read(iprot);" << endl;
+ } else {
+ out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent()
+ << prefix << ".Read(iprot);" << endl;
+ }
+}
+
+void t_csharp_generator::generate_deserialize_container(ostream& out,
+ t_type* ttype,
+ string prefix) {
+ scope_up(out);
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ indent(out) << prefix << " = new " << type_name(ttype, false, true) << "();" << endl;
+ if (ttype->is_map()) {
+ out << indent() << "TMap " << obj << " = iprot.ReadMapBegin();" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "TSet " << obj << " = iprot.ReadSetBegin();" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "TList " << obj << " = iprot.ReadListBegin();" << endl;
+ }
+
+ string i = tmp("_i");
+ indent(out) << "for( int " << i << " = 0; " << i << " < " << obj << ".Count"
+ << "; "
+ << "++" << i << ")" << endl;
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "iprot.ReadMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.ReadSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot.ReadListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+void t_csharp_generator::generate_deserialize_map_element(ostream& out,
+ t_map* tmap,
+ string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey) << endl;
+ indent(out) << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+void t_csharp_generator::generate_deserialize_set_element(ostream& out,
+ t_set* tset,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_csharp_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_csharp_generator::generate_serialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool is_element,
+ bool is_propertyless) {
+ t_type* type = tfield->get_type();
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ string name = prefix + (is_propertyless ? "" : prop_name(tfield));
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << "oprot.";
+
+ string nullable_name = nullable_ && !is_element && !field_is_required(tfield) ? name + ".Value"
+ : name;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "WriteBinary(";
+ } else {
+ out << "WriteString(";
+ }
+ out << name << ");";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "WriteBool(" << nullable_name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "WriteByte(" << nullable_name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "WriteI16(" << nullable_name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "WriteI32(" << nullable_name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "WriteI64(" << nullable_name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "WriteDouble(" << nullable_name << ");";
+ break;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "WriteI32((int)" << nullable_name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+void t_csharp_generator::generate_serialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+ (void)tstruct;
+ out << indent() << prefix << ".Write(oprot);" << endl;
+}
+
+void t_csharp_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.WriteMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix
+ << ".Count));" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.WriteSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", " << prefix << ".Count));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.WriteListBegin(new TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".Count));"
+ << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map()) {
+ indent(out) << "foreach (" << type_name(((t_map*)ttype)->get_key_type()) << " " << iter
+ << " in " << prefix << ".Keys)";
+ } else if (ttype->is_set()) {
+ indent(out) << "foreach (" << type_name(((t_set*)ttype)->get_elem_type()) << " " << iter
+ << " in " << prefix << ")";
+ } else if (ttype->is_list()) {
+ indent(out) << "foreach (" << type_name(((t_list*)ttype)->get_elem_type()) << " " << iter
+ << " in " << prefix << ")";
+ }
+
+ out << endl;
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter, prefix);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ }
+
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.WriteMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.WriteSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.WriteListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+void t_csharp_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string iter,
+ string map) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "", true);
+ t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+ generate_serialize_field(out, &vfield, "", true);
+}
+
+void t_csharp_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "", true);
+}
+
+void t_csharp_generator::generate_serialize_list_element(ostream& out,
+ t_list* tlist,
+ string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "", true);
+}
+
+void t_csharp_generator::generate_property(ostream& out,
+ t_field* tfield,
+ bool isPublic,
+ bool generateIsset) {
+ generate_csharp_property(out, tfield, isPublic, generateIsset, "_");
+}
+void t_csharp_generator::generate_csharp_property(ostream& out,
+ t_field* tfield,
+ bool isPublic,
+ bool generateIsset,
+ std::string fieldPrefix) {
+ if ((serialize_ || wcf_) && isPublic) {
+ indent(out) << "[DataMember(Order = 0)]" << endl;
+ }
+ bool has_default = field_has_default(tfield);
+ bool is_required = field_is_required(tfield);
+ if ((nullable_ && !has_default) || (is_required)) {
+ indent(out) << (isPublic ? "public " : "private ")
+ << type_name(tfield->get_type(), false, false, true, is_required) << " "
+ << prop_name(tfield) << " { get; set; }" << endl;
+ } else {
+ indent(out) << (isPublic ? "public " : "private ")
+ << type_name(tfield->get_type(), false, false, true) << " " << prop_name(tfield)
+ << endl;
+ scope_up(out);
+ indent(out) << "get" << endl;
+ scope_up(out);
+ bool use_nullable = false;
+ if (nullable_) {
+ t_type* ttype = tfield->get_type();
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+ if (ttype->is_base_type()) {
+ use_nullable = ((t_base_type*)ttype)->get_base() != t_base_type::TYPE_STRING;
+ } else if (ttype->is_enum()) {
+ use_nullable = true;
+ }
+ }
+ indent(out) << "return " << fieldPrefix + tfield->get_name() << ";" << endl;
+ scope_down(out);
+ indent(out) << "set" << endl;
+ scope_up(out);
+ if (use_nullable) {
+ if (generateIsset) {
+ indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;"
+ << endl;
+ }
+ indent(out) << "if (value.HasValue) this." << fieldPrefix + tfield->get_name()
+ << " = value.Value;" << endl;
+ } else {
+ if (generateIsset) {
+ indent(out) << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl;
+ }
+ indent(out) << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl;
+ }
+ scope_down(out);
+ scope_down(out);
+ }
+ out << endl;
+}
+
+std::string t_csharp_generator::make_valid_csharp_identifier(std::string const& fromName) {
+ std::string str = fromName;
+ if (str.empty()) {
+ return str;
+ }
+
+ // tests rely on this
+ assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+ // if the first letter is a number, we add an additional underscore in front of it
+ char c = str.at(0);
+ if (('0' <= c) && (c <= '9')) {
+ str = "_" + str;
+ }
+
+ // following chars: letter, number or underscore
+ for (size_t i = 0; i < str.size(); ++i) {
+ c = str.at(i);
+ if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
+ && ('_' != c)) {
+ str.replace(i, 1, "_");
+ }
+ }
+
+ return str;
+}
+
+void t_csharp_generator::cleanup_member_name_mapping(void* scope) {
+ if( member_mapping_scopes.empty()) {
+ throw "internal error: cleanup_member_name_mapping() no scope active";
+ }
+
+ member_mapping_scope& active = member_mapping_scopes.back();
+ if (active.scope_member != scope) {
+ throw "internal error: cleanup_member_name_mapping() called for wrong struct";
+ }
+
+ member_mapping_scopes.pop_back();
+}
+
+string t_csharp_generator::get_mapped_member_name(string name) {
+ if( ! member_mapping_scopes.empty()) {
+ member_mapping_scope& active = member_mapping_scopes.back();
+ map<string, string>::iterator iter = active.mapping_table.find(name);
+ if (active.mapping_table.end() != iter) {
+ return iter->second;
+ }
+ }
+
+ pverbose("no mapping for member %s\n", name.c_str());
+ return name;
+}
+
+void t_csharp_generator::prepare_member_name_mapping(t_struct* tstruct) {
+ prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name());
+}
+
+void t_csharp_generator::prepare_member_name_mapping(void* scope,
+ const vector<t_field*>& members,
+ const string& structname) {
+ // begin new scope
+ member_mapping_scope dummy;
+ dummy.scope_member = 0;
+ member_mapping_scopes.push_back(dummy);
+ member_mapping_scope& active = member_mapping_scopes.back();
+ active.scope_member = scope;
+
+ // current C# generator policy:
+ // - prop names are always rendered with an Uppercase first letter
+ // - struct names are used as given
+ std::set<std::string> used_member_names;
+ vector<t_field*>::const_iterator iter;
+
+ // prevent name conflicts with struct (CS0542 error)
+ used_member_names.insert(structname);
+
+ // prevent name conflicts with known methods (THRIFT-2942)
+ used_member_names.insert("Read");
+ used_member_names.insert("Write");
+
+ for (iter = members.begin(); iter != members.end(); ++iter) {
+ string oldname = (*iter)->get_name();
+ string newname = prop_name(*iter, true);
+ while (true) {
+
+ // new name conflicts with another member
+ if (used_member_names.find(newname) != used_member_names.end()) {
+ pverbose("struct %s: member %s conflicts with another member\n",
+ structname.c_str(),
+ newname.c_str());
+ newname += '_';
+ continue;
+ }
+
+ // add always, this helps us to detect edge cases like
+ // different spellings ("foo" and "Foo") within the same struct
+ pverbose("struct %s: member mapping %s => %s\n",
+ structname.c_str(),
+ oldname.c_str(),
+ newname.c_str());
+ active.mapping_table[oldname] = newname;
+ used_member_names.insert(newname);
+ break;
+ }
+ }
+}
+
+std::string t_csharp_generator::prop_name(t_field* tfield, bool suppress_mapping) {
+ string name(tfield->get_name());
+ if (suppress_mapping) {
+ name[0] = toupper(name[0]);
+ } else {
+ name = get_mapped_member_name(name);
+ }
+ return name;
+}
+
+string t_csharp_generator::type_name(t_type* ttype,
+ bool in_container,
+ bool in_init,
+ bool in_param,
+ bool is_required) {
+ (void)in_init;
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype, in_container, in_param, is_required);
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ return "Dictionary<" + type_name(tmap->get_key_type(), true) + ", "
+ + type_name(tmap->get_val_type(), true) + ">";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ return "THashSet<" + type_name(tset->get_elem_type(), true) + ">";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ return "List<" + type_name(tlist->get_elem_type(), true) + ">";
+ }
+
+ t_program* program = ttype->get_program();
+ string postfix = (!is_required && nullable_ && in_param && ttype->is_enum()) ? "?" : "";
+ if (program != NULL && program != program_) {
+ string ns = program->get_namespace("csharp");
+ if (!ns.empty()) {
+ return ns + "." + normalize_name(ttype->get_name()) + postfix;
+ }
+ }
+
+ return normalize_name(ttype->get_name()) + postfix;
+}
+
+string t_csharp_generator::base_type_name(t_base_type* tbase,
+ bool in_container,
+ bool in_param,
+ bool is_required) {
+ (void)in_container;
+ string postfix = (!is_required && nullable_ && in_param) ? "?" : "";
+ switch (tbase->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ if (tbase->is_binary()) {
+ return "byte[]";
+ } else {
+ return "string";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "bool" + postfix;
+ case t_base_type::TYPE_I8:
+ return "sbyte" + postfix;
+ case t_base_type::TYPE_I16:
+ return "short" + postfix;
+ case t_base_type::TYPE_I32:
+ return "int" + postfix;
+ case t_base_type::TYPE_I64:
+ return "long" + postfix;
+ case t_base_type::TYPE_DOUBLE:
+ return "double" + postfix;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base());
+ }
+}
+
+string t_csharp_generator::declare_field(t_field* tfield, bool init, std::string prefix) {
+ string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name();
+ if (init) {
+ t_type* ttype = tfield->get_type();
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+ if (ttype->is_base_type() && field_has_default(tfield)) {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
+ } else if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+ } else if (ttype->is_enum()) {
+ result += " = (" + type_name(ttype, false, true) + ")0";
+ } else if (ttype->is_container()) {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ } else {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ }
+ }
+ return result + ";";
+}
+
+string t_csharp_generator::function_signature(t_function* tfunction, string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "("
+ + argument_list(tfunction->get_arglist()) + ")";
+}
+
+string t_csharp_generator::function_signature_async_begin(t_function* tfunction, string prefix) {
+ string comma = (tfunction->get_arglist()->get_members().size() > 0 ? ", " : "");
+ return "IAsyncResult " + normalize_name(prefix + tfunction->get_name())
+ + "(AsyncCallback callback, object state" + comma + argument_list(tfunction->get_arglist())
+ + ")";
+}
+
+string t_csharp_generator::function_signature_async_end(t_function* tfunction, string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name())
+ + "(IAsyncResult asyncResult)";
+}
+
+string t_csharp_generator::function_signature_async(t_function* tfunction, string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ string task = "Task";
+ if (!ttype->is_void())
+ task += "<" + type_name(ttype) + ">";
+ return task + " " + normalize_name(prefix + tfunction->get_name()) + "Async("
+ + argument_list(tfunction->get_arglist()) + ")";
+}
+
+string t_csharp_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name());
+ }
+ return result;
+}
+
+string t_csharp_generator::type_to_enum(t_type* type) {
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.String";
+ case t_base_type::TYPE_BOOL:
+ return "TType.Bool";
+ case t_base_type::TYPE_I8:
+ return "TType.Byte";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.Double";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.Struct";
+ } else if (type->is_map()) {
+ return "TType.Map";
+ } else if (type->is_set()) {
+ return "TType.Set";
+ } else if (type->is_list()) {
+ return "TType.List";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+void t_csharp_generator::generate_csharp_docstring_comment(ostream& out, string contents) {
+ generate_docstring_comment(out, "/// <summary>\n", "/// ", contents, "/// </summary>\n");
+}
+
+void t_csharp_generator::generate_csharp_doc(ostream& out, t_field* field) {
+ if (field->get_type()->is_enum()) {
+ string combined_message = field->get_doc() + "\n<seealso cref=\""
+ + get_enum_class_name(field->get_type()) + "\"/>";
+ generate_csharp_docstring_comment(out, combined_message);
+ } else {
+ generate_csharp_doc(out, (t_doc*)field);
+ }
+}
+
+void t_csharp_generator::generate_csharp_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_csharp_docstring_comment(out, tdoc->get_doc());
+ }
+}
+
+void t_csharp_generator::generate_csharp_doc(ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc()) {
+ stringstream ps;
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ps << "\n<param name=\"" << p->get_name() << "\">";
+ if (p->has_doc()) {
+ std::string str = p->get_doc();
+ str.erase(std::remove(str.begin(), str.end(), '\n'),
+ str.end()); // remove the newlines that appear from the parser
+ ps << str;
+ }
+ ps << "</param>";
+ }
+ generate_docstring_comment(out,
+ "",
+ "/// ",
+ "<summary>\n" + tfunction->get_doc() + "</summary>" + ps.str(),
+ "");
+ }
+}
+
+std::string t_csharp_generator::get_enum_class_name(t_type* type) {
+ string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL && program != program_) {
+ package = program->get_namespace("csharp") + ".";
+ }
+ return package + type->get_name();
+}
+
+
+THRIFT_REGISTER_GENERATOR(
+ csharp,
+ "C#",
+ " async: Adds Async support using Task.Run.\n"
+ " wcf: Adds bindings for WCF to generated classes.\n"
+ " serial: Add serialization support to generated classes.\n"
+ " nullable: Use nullable types for properties.\n"
+ " hashcode: Generate a hashcode and equals implementation for classes.\n"
+ " union: Use new union typing, which includes a static read function for union "
+ "types.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc
new file mode 100644
index 000000000..65f4b445e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc
@@ -0,0 +1,774 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <sys/stat.h>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::ostringstream;
+using std::set;
+using std::string;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * D code generator.
+ *
+ * generate_*() functions are called by the base class to emit code for the
+ * given entity, print_*() functions write a piece of code to the passed
+ * stream, and render_*() return a string containing the D representation of
+ * the passed entity.
+ */
+class t_d_generator : public t_oop_generator {
+public:
+ t_d_generator(t_program* program,
+ const std::map<string, string>& parsed_options,
+ const string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option d:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-d";
+ }
+
+protected:
+
+ // D reserved words are suffixed with an underscore
+ static string suffix_if_reserved(const string& name) {
+ const bool isIn = std::binary_search(std::begin(d_reserved_words), std::end(d_reserved_words), name);
+ string ret = isIn ? name + "_" : name;
+ return ret;
+ }
+
+ void init_generator() override {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ string dir = program_->get_namespace("d");
+ string subdir = get_out_dir();
+ string::size_type loc;
+ while ((loc = dir.find(".")) != string::npos) {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (!dir.empty()) {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ package_dir_ = subdir + "/";
+
+ // Make output file
+ string f_types_name = package_dir_ + program_name_ + "_types.d";
+ f_types_.open(f_types_name.c_str());
+
+ // Print header
+ f_types_ << autogen_comment() << "module " << render_package(*program_) << program_name_
+ << "_types;" << endl << endl;
+
+ print_default_imports(f_types_);
+
+ // Include type modules from other imported programs.
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ f_types_ << "public import " << render_package(*include) << include->get_name()
+ << "_types;" << endl;
+ }
+ if (!includes.empty())
+ f_types_ << endl;
+ }
+
+ void close_generator() override {
+ // Close output file
+ f_types_.close();
+ }
+
+ void generate_consts(std::vector<t_const*> consts) override {
+ if (!consts.empty()) {
+ string f_consts_name = package_dir_ + program_name_ + "_constants.d";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ f_consts << autogen_comment() << "module " << render_package(*program_) << program_name_
+ << "_constants;" << endl << endl;
+
+ print_default_imports(f_consts);
+
+ f_consts << "import " << render_package(*get_program()) << program_name_ << "_types;" << endl
+ << endl;
+
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ this->emit_doc(*c_iter, f_consts);
+ string name = suffix_if_reserved((*c_iter)->get_name());
+ t_type* type = (*c_iter)->get_type();
+ indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << endl;
+ }
+
+ f_consts << endl << "shared static this() {" << endl;
+ indent_up();
+
+ bool first = true;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_consts << endl;
+ }
+ t_type* type = (*c_iter)->get_type();
+ indent(f_consts) << suffix_if_reserved((*c_iter)->get_name()) << " = ";
+ if (!is_immutable_type(type)) {
+ f_consts << "cast(immutable(" << render_type_name(type) << ")) ";
+ }
+ f_consts << render_const_value(type, (*c_iter)->get_value()) << ";" << endl;
+ }
+ indent_down();
+ indent(f_consts) << "}" << endl;
+ }
+ }
+
+ void generate_typedef(t_typedef* ttypedef) override {
+ this->emit_doc(ttypedef, f_types_);
+ f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " "
+ << ttypedef->get_symbolic() << ";" << endl << endl;
+ }
+
+ void generate_enum(t_enum* tenum) override {
+ vector<t_enum_value*> constants = tenum->get_constants();
+
+ this->emit_doc(tenum, f_types_);
+ string enum_name = suffix_if_reserved(tenum->get_name());
+ f_types_ << indent() << "enum " << enum_name << " {" << endl;
+
+ indent_up();
+
+ vector<t_enum_value*>::const_iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ this->emit_doc(*c_iter, f_types_);
+ indent(f_types_) << suffix_if_reserved((*c_iter)->get_name());
+ f_types_ << " = " << (*c_iter)->get_value() << ",";
+ }
+
+ f_types_ << endl;
+ indent_down();
+ indent(f_types_) << "}" << endl;
+
+ f_types_ << endl;
+ }
+
+ void generate_struct(t_struct* tstruct) override {
+ print_struct_definition(f_types_, tstruct, false);
+ }
+
+ void generate_xception(t_struct* txception) override {
+ print_struct_definition(f_types_, txception, true);
+ }
+
+ void generate_service(t_service* tservice) override {
+ string svc_name = suffix_if_reserved(tservice->get_name());
+
+ // Service implementation file includes
+ string f_servicename = package_dir_ + svc_name + ".d";
+ ofstream_with_content_based_conditional_update f_service;
+ f_service.open(f_servicename.c_str());
+ f_service << autogen_comment() << "module " << suffix_if_reserved(render_package(*program_)) << svc_name << ";"
+ << endl << endl;
+
+ print_default_imports(f_service);
+
+ f_service << "import " << suffix_if_reserved(render_package(*get_program())) << program_name_ << "_types;" << endl;
+
+ t_service* extends_service = tservice->get_extends();
+ if (extends_service != NULL) {
+ f_service << "import " << suffix_if_reserved(render_package(*(extends_service->get_program())))
+ << suffix_if_reserved(extends_service->get_name()) << ";" << endl;
+ }
+
+ f_service << endl;
+
+ string extends = "";
+ if (tservice->get_extends() != NULL) {
+ extends = " : " + suffix_if_reserved(render_type_name(tservice->get_extends()));
+ }
+
+ this->emit_doc(tservice, f_service);
+ f_service << indent() << "interface " << svc_name << extends << " {" << endl;
+ indent_up();
+
+ // Collect all the exception types service methods can throw so we can
+ // emit the necessary aliases later.
+ set<t_type*> exception_types;
+
+ // Print the method signatures.
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator fn_iter;
+ for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
+ this->emit_doc(*fn_iter, f_service);
+ f_service << indent();
+ print_function_signature(f_service, *fn_iter);
+ f_service << ";" << endl;
+
+ const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator ex_iter;
+ for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) {
+ exception_types.insert((*ex_iter)->get_type());
+ }
+ }
+
+ // Alias the exception types into the current scope.
+ if (!exception_types.empty())
+ f_service << endl;
+ set<t_type*>::const_iterator et_iter;
+ for (et_iter = exception_types.begin(); et_iter != exception_types.end(); ++et_iter) {
+ indent(f_service) << "alias " << render_package(*(*et_iter)->get_program())
+ << (*et_iter)->get_program()->get_name() << "_types"
+ << "." << (*et_iter)->get_name() << " " << (*et_iter)->get_name() << ";"
+ << endl;
+ }
+
+ // Write the method metadata.
+ ostringstream meta;
+ indent_up();
+ bool first = true;
+ for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
+ if ((*fn_iter)->get_arglist()->get_members().empty()
+ && (*fn_iter)->get_xceptions()->get_members().empty() && !(*fn_iter)->is_oneway()) {
+ continue;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ meta << ",";
+ }
+
+ meta << endl << indent() << "TMethodMeta(`" << suffix_if_reserved((*fn_iter)->get_name()) << "`, " << endl;
+ indent_up();
+ indent(meta) << "[";
+
+ bool first = true;
+ const vector<t_field*>& params = (*fn_iter)->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = params.begin(); p_iter != params.end(); ++p_iter) {
+ if (first) {
+ first = false;
+ } else {
+ meta << ", ";
+ }
+
+ meta << "TParamMeta(`" << suffix_if_reserved((*p_iter)->get_name()) << "`, " << (*p_iter)->get_key();
+
+ t_const_value* cv = (*p_iter)->get_value();
+ if (cv != NULL) {
+ meta << ", q{" << render_const_value((*p_iter)->get_type(), cv) << "}";
+ }
+ meta << ")";
+ }
+
+ meta << "]";
+
+ if (!(*fn_iter)->get_xceptions()->get_members().empty() || (*fn_iter)->is_oneway()) {
+ meta << "," << endl << indent() << "[";
+
+ bool first = true;
+ const vector<t_field*>& exceptions = (*fn_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator ex_iter;
+ for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) {
+ if (first) {
+ first = false;
+ } else {
+ meta << ", ";
+ }
+
+ meta << "TExceptionMeta(`" << suffix_if_reserved((*ex_iter)->get_name()) << "`, "
+ << (*ex_iter)->get_key() << ", `" << (*ex_iter)->get_type()->get_name() << "`)";
+ }
+
+ meta << "]";
+ }
+
+ if ((*fn_iter)->is_oneway()) {
+ meta << "," << endl << indent() << "TMethodType.ONEWAY";
+ }
+
+ indent_down();
+ meta << endl << indent() << ")";
+ }
+ indent_down();
+
+ string meta_str(meta.str());
+ if (!meta_str.empty()) {
+ f_service << endl << indent() << "enum methodMeta = [" << meta_str << endl << indent() << "];"
+ << endl;
+ }
+
+ indent_down();
+ indent(f_service) << "}" << endl;
+
+ // Server skeleton generation.
+ string f_skeletonname = package_dir_ + svc_name + "_server.skeleton.d";
+ ofstream_with_content_based_conditional_update f_skeleton;
+ f_skeleton.open(f_skeletonname.c_str());
+ print_server_skeleton(f_skeleton, tservice);
+ f_skeleton.close();
+ }
+
+ void emit_doc(t_doc *doc, std::ostream& out) {
+ if (!doc->has_doc()) {
+ return;
+ }
+ indent(out) << "/**" << std::endl;
+ indent_up();
+ // No endl -- comments reliably have a newline at the end.
+ // This is true even for stuff like:
+ // /** method infos */ void foo(/** huh?*/ 1: i64 stuff)
+ indent(out) << doc->get_doc();
+ indent_down();
+ indent(out) << "*/" << std::endl;
+ }
+
+private:
+ /**
+ * Writes a server skeleton for the passed service to out.
+ */
+ void print_server_skeleton(ostream& out, t_service* tservice) {
+ string svc_name = suffix_if_reserved(tservice->get_name());
+
+ out << "/*" << endl
+ << " * This auto-generated skeleton file illustrates how to build a server. If you" << endl
+ << " * intend to customize it, you should edit a copy with another file name to " << endl
+ << " * avoid overwriting it when running the generator again." << endl << " */" << endl
+ << "module " << render_package(*tservice->get_program()) << svc_name << "_server;" << endl
+ << endl << "import std.stdio;" << endl << "import thrift.codegen.processor;" << endl
+ << "import thrift.protocol.binary;" << endl << "import thrift.server.simple;" << endl
+ << "import thrift.server.transport.socket;" << endl << "import thrift.transport.buffered;"
+ << endl << "import thrift.util.hashset;" << endl << endl << "import "
+ << render_package(*tservice->get_program()) << svc_name << ";" << endl << "import "
+ << render_package(*get_program()) << program_name_ << "_types;" << endl << endl << endl
+ << "class " << svc_name << "Handler : " << svc_name << " {" << endl;
+
+ indent_up();
+ out << indent() << "this() {" << endl << indent() << " // Your initialization goes here."
+ << endl << indent() << "}" << endl << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ out << indent();
+ print_function_signature(out, *f_iter);
+ out << " {" << endl;
+
+ indent_up();
+
+ out << indent() << "// Your implementation goes here." << endl << indent() << "writeln(\""
+ << suffix_if_reserved((*f_iter)->get_name()) << " called\");" << endl;
+
+ t_type* rt = (*f_iter)->get_returntype();
+ if (!rt->is_void()) {
+ indent(out) << "return typeof(return).init;" << endl;
+ }
+
+ indent_down();
+
+ out << indent() << "}" << endl << endl;
+ }
+
+ indent_down();
+ out << "}" << endl << endl;
+
+ out << indent() << "void main() {" << endl;
+ indent_up();
+ out << indent() << "auto protocolFactory = new TBinaryProtocolFactory!();" << endl << indent()
+ << "auto processor = new TServiceProcessor!" << svc_name << "(new " << svc_name
+ << "Handler);" << endl << indent() << "auto serverTransport = new TServerSocket(9090);"
+ << endl << indent() << "auto transportFactory = new TBufferedTransportFactory;" << endl
+ << indent() << "auto server = new TSimpleServer(" << endl << indent()
+ << " processor, serverTransport, transportFactory, protocolFactory);" << endl << indent()
+ << "server.serve();" << endl;
+ indent_down();
+ out << "}" << endl;
+ }
+
+ /**
+ * Writes the definition of a struct or an exception type to out.
+ */
+ void print_struct_definition(ostream& out, t_struct* tstruct, bool is_exception) {
+ const vector<t_field*>& members = tstruct->get_members();
+
+ if (is_exception) {
+ indent(out) << "class " << suffix_if_reserved(tstruct->get_name()) << " : TException {" << endl;
+ } else {
+ indent(out) << "struct " << suffix_if_reserved(tstruct->get_name()) << " {" << endl;
+ }
+ indent_up();
+
+ // Declare all fields.
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << render_type_name((*m_iter)->get_type()) << " " << suffix_if_reserved((*m_iter)->get_name()) << ";"
+ << endl;
+ }
+
+ if (!members.empty())
+ indent(out) << endl;
+ indent(out) << "mixin TStructHelpers!(";
+
+ if (!members.empty()) {
+ // If there are any fields, construct the TFieldMeta array to pass to
+ // TStructHelpers. We can't just pass an empty array if not because []
+ // doesn't pass the TFieldMeta[] constraint.
+ out << "[";
+ indent_up();
+
+ bool first = true;
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ",";
+ }
+ out << endl;
+
+ indent(out) << "TFieldMeta(`" << suffix_if_reserved((*m_iter)->get_name()) << "`, " << (*m_iter)->get_key();
+
+ t_const_value* cv = (*m_iter)->get_value();
+ t_field::e_req req = (*m_iter)->get_req();
+ out << ", " << render_req(req);
+ if (cv != NULL) {
+ out << ", q{" << render_const_value((*m_iter)->get_type(), cv) << "}";
+ }
+ out << ")";
+ }
+
+ indent_down();
+ out << endl << indent() << "]";
+ }
+
+ out << ");" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ /**
+ * Prints the D function signature (including return type) for the given
+ * method.
+ */
+ void print_function_signature(ostream& out, t_function* fn) {
+ out << render_type_name(fn->get_returntype()) << " " << suffix_if_reserved(fn->get_name()) << "(";
+
+ const vector<t_field*>& fields = fn->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ", ";
+ }
+ out << render_type_name((*f_iter)->get_type(), true) << " " << suffix_if_reserved((*f_iter)->get_name());
+ }
+
+ out << ")";
+ }
+
+ /**
+ * Returns the D representation of value. The result is guaranteed to be a
+ * single expression; for complex types, immediately called delegate
+ * literals are used to achieve this.
+ */
+ string render_const_value(t_type* type, t_const_value* value) {
+ // Resolve any typedefs.
+ type = get_true_type(type);
+
+ ostringstream out;
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ out << "cast(" << render_type_name(type) << ")" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ out << value->get_integer() << "L";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "Compiler error: No const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "cast(" << render_type_name(type) << ")" << value->get_integer();
+ } else {
+ out << "{" << endl;
+ indent_up();
+
+ indent(out) << render_type_name(type) << " v;" << endl;
+ if (type->is_struct() || type->is_xception()) {
+ indent(out) << "v = " << (type->is_xception() ? "new " : "") << render_type_name(type)
+ << "();" << endl;
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "Type error: " + type->get_name() + " has no field "
+ + v_iter->first->get_string();
+ }
+ string val = render_const_value(field_type, v_iter->second);
+ indent(out) << "v.set!`" << v_iter->first->get_string() << "`(" << val << ");" << endl;
+ }
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(ktype, v_iter->first);
+ string val = render_const_value(vtype, v_iter->second);
+ indent(out) << "v[";
+ if (!is_immutable_type(ktype)) {
+ out << "cast(immutable(" << render_type_name(ktype) << "))";
+ }
+ out << key << "] = " << val << ";" << endl;
+ }
+ } else if (type->is_list()) {
+ t_type* etype = ((t_list*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(etype, *v_iter);
+ indent(out) << "v ~= " << val << ";" << endl;
+ }
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(etype, *v_iter);
+ indent(out) << "v ~= " << val << ";" << endl;
+ }
+ } else {
+ throw "Compiler error: Invalid type in render_const_value: " + type->get_name();
+ }
+ indent(out) << "return v;" << endl;
+
+ indent_down();
+ indent(out) << "}()";
+ }
+
+ return out.str();
+ }
+
+ /**
+ * Returns the D package to which modules for program are written (with a
+ * trailing dot, if not empty).
+ */
+ string render_package(const t_program& program) const {
+ string package = program.get_namespace("d");
+ if (package.size() == 0)
+ return "";
+ return package + ".";
+ }
+
+ /**
+ * Returns the name of the D repesentation of ttype.
+ *
+ * If isArg is true, a const reference to the type will be returned for
+ * structs.
+ */
+ string render_type_name(const t_type* ttype, bool isArg = false) const {
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ return "string";
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "byte";
+ case t_base_type::TYPE_I16:
+ return "short";
+ case t_base_type::TYPE_I32:
+ return "int";
+ case t_base_type::TYPE_I64:
+ return "long";
+ case t_base_type::TYPE_DOUBLE:
+ return "double";
+ default:
+ throw "Compiler error: No D type name for base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+
+ if (ttype->is_container()) {
+ t_container* tcontainer = (t_container*)ttype;
+ if (tcontainer->has_cpp_name()) {
+ return tcontainer->get_cpp_name();
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ t_type* ktype = tmap->get_key_type();
+
+ string name = render_type_name(tmap->get_val_type()) + "[";
+ if (!is_immutable_type(ktype)) {
+ name += "immutable(";
+ }
+ name += render_type_name(ktype);
+ if (!is_immutable_type(ktype)) {
+ name += ")";
+ }
+ name += "]";
+ return name;
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ return "HashSet!(" + render_type_name(tset->get_elem_type()) + ")";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ return render_type_name(tlist->get_elem_type()) + "[]";
+ }
+ }
+
+ if (ttype->is_struct() && isArg) {
+ return "ref const(" + ttype->get_name() + ")";
+ } else {
+ return ttype->get_name();
+ }
+ }
+
+ /**
+ * Returns the D TReq enum member corresponding to req.
+ */
+ string render_req(t_field::e_req req) const {
+ switch (req) {
+ case t_field::T_OPT_IN_REQ_OUT:
+ return "TReq.OPT_IN_REQ_OUT";
+ case t_field::T_OPTIONAL:
+ return "TReq.OPTIONAL";
+ case t_field::T_REQUIRED:
+ return "TReq.REQUIRED";
+ default: {
+ std::stringstream ss;
+ ss << "Compiler error: Invalid requirement level " << req;
+ throw ss.str();
+ }
+ }
+ }
+
+ /**
+ * Writes the default list of imports (which are written to every generated
+ * module) to f.
+ */
+ void print_default_imports(ostream& out) {
+ indent(out) << "import thrift.base;" << endl << "import thrift.codegen.base;" << endl
+ << "import thrift.util.hashset;" << endl << endl;
+ }
+
+ /**
+ * Returns whether type is »intrinsically immutable«, in the sense that
+ * a value of that type is implicitly castable to immutable(type), and it is
+ * allowed for AA keys without an immutable() qualifier.
+ */
+ bool is_immutable_type(t_type* type) const {
+ t_type* ttype = get_true_type(type);
+ return ttype->is_base_type() || ttype->is_enum();
+ }
+
+ /*
+ * File streams, stored here to avoid passing them as parameters to every
+ * function.
+ */
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_header_;
+
+ string package_dir_;
+
+ protected:
+ static vector<string> d_reserved_words;
+
+};
+
+vector<string> t_d_generator::d_reserved_words = {
+ // The keywords are extracted from https://dlang.org/spec/lex.html
+ // and sorted for use with std::binary_search
+ "__FILE_FULL_PATH__", "__FILE__", "__FUNCTION__", "__LINE__", "__MODULE__",
+ "__PRETTY_FUNCTION__", "__gshared", "__parameters", "__traits", "__vector",
+ "abstract", "alias", "align", "asm", "assert", "auto", "body", "bool",
+ "break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat",
+ "char", "class", "const", "continue", "creal", "dchar", "debug", "default",
+ "delegate", "delete", "deprecated", "do", "double", "else", "enum",
+ "export", "extern", "false", "final", "finally", "float", "for", "foreach",
+ "foreach_reverse", "function", "goto", "idouble", "if", "ifloat", "immutable",
+ "import", "in", "inout", "int", "interface", "invariant", "ireal", "is",
+ "lazy", "long", "macro ", "mixin", "module", "new", "nothrow", "null", "out",
+ "override", "package", "pragma", "private", "protected", "public", "pure",
+ "real", "ref", "return", "scope", "shared", "short", "static", "struct",
+ "super", "switch", "synchronized", "template", "this", "throw", "true", "try",
+ "typeid", "typeof", "ubyte", "ucent", "uint", "ulong", "union", "unittest",
+ "ushort", "version", "void", "wchar", "while", "with"
+};
+
+THRIFT_REGISTER_GENERATOR(d, "D", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc
new file mode 100644
index 000000000..fbbb9e6d3
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc
@@ -0,0 +1,2514 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <sys/stat.h>
+#include <stdexcept>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+static const string endl2 = "\n\n";
+
+/**
+ * Use the current Thrift version for static libraries. When releasing, update
+ * the version in these files.
+ * - lib/dart/pubspec.yaml
+ * - test/dart/test_client/pubspec.yaml
+ * - tutorial/dart/client/pubspec.yaml
+ * - tutorial/dart/console_client/pubspec.yaml
+ * - tutorial/dart/server/pubspec.yaml
+ * See https://thrift.apache.org/docs/committers/HowToVersion
+ */
+static const string dart_thrift_version = THRIFT_VERSION;
+
+/* forward declarations */
+string initial_caps_to_underscores(string name);
+
+/**
+ * Dart code generator
+ *
+ */
+class t_dart_generator : public t_oop_generator {
+public:
+ t_dart_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ library_name_ = "";
+ library_prefix_ = "";
+ package_prefix_ = "";
+ pubspec_lib_ = "";
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("library_name") == 0) {
+ library_name_ = (iter->second);
+ } else if( iter->first.compare("library_prefix") == 0) {
+ library_prefix_ = (iter->second) + ".";
+ package_prefix_ = replace_all(library_prefix_, ".", "/");
+ } else if( iter->first.compare("pubspec_lib") == 0) {
+ pubspec_lib_ = (iter->second);
+ } else {
+ throw "unknown option dart:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-dart";
+ }
+
+ void scope_up(std::ostream& out, std::string prefix=" ") {
+ out << prefix << "{" << endl;
+ indent_up();
+ }
+
+ void scope_down(std::ostream& out, std::string postfix=endl) {
+ indent_down();
+ indent(out) << "}" << postfix;
+ }
+
+ string replace_all(string contents, string search, string repl) {
+ string str(contents);
+
+ size_t slen = search.length();
+ size_t rlen = repl.length();
+ size_t incr = (rlen > 0) ? rlen : 1;
+
+ if (slen > 0) {
+ size_t found = str.find(search);
+ while ((found != string::npos) && (found < str.length())) {
+ str.replace(found, slen, repl);
+ found = str.find(search, found + incr);
+ }
+ }
+
+ return str;
+ }
+
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void export_class_to_library(string file_name, string class_name);
+
+ void generate_dart_library();
+ void generate_dart_pubspec();
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ void print_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval = false);
+ std::string render_const_value(ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_dart_struct(t_struct* tstruct, bool is_exception);
+
+ void generate_dart_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool is_result = false,
+ string export_file_name = "");
+ void generate_dart_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_dart_validator(std::ostream& out, t_struct* tstruct);
+ void generate_dart_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_dart_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_dart_struct_tostring(std::ostream& out, t_struct* tstruct);
+ std::string get_dart_type_string(t_type* type);
+ void generate_generic_field_getters(std::ostream& out, t_struct* tstruct);
+ void generate_generic_field_setters(std::ostream& out, t_struct* tstruct);
+ void generate_generic_isset_method(std::ostream& out, t_struct* tstruct);
+ void generate_dart_bean_boilerplate(std::ostream& out, t_struct* tstruct);
+
+ void generate_function_helpers(t_function* tfunction);
+ std::string init_value(t_field* tfield);
+ std::string get_cap_name(std::string name);
+ std::string get_member_name(std::string name);
+ std::string get_args_class_name(std::string name);
+ std::string get_result_class_name(std::string name);
+ std::string get_file_name(std::string name);
+ std::string get_constants_class_name(std::string name);
+ std::string generate_isset_check(t_field* field);
+ std::string generate_isset_check(std::string field);
+ void generate_isset_set(ostream& out, t_field* field);
+
+ void generate_service_interface(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string iter,
+ std::string map);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_dart_doc(std::ostream& out, t_doc* tdoc);
+
+ void generate_dart_doc(std::ostream& out, t_function* tdoc);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string find_library_name(t_program* program);
+ std::string dart_library(string file_name);
+ std::string service_imports();
+ std::string dart_thrift_imports();
+ std::string type_name(t_type* ttype);
+ std::string base_type_name(t_base_type* tbase);
+ std::string declare_field(t_field* tfield, bool init = false);
+ std::string function_signature(t_function* tfunction);
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string get_ttype_class_name(t_type* ttype);
+
+ bool type_can_be_null(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception()
+ || ttype->is_string();
+ }
+
+ vector<std::string> split(const string& s, char delim) {
+ vector<std::string> elems;
+ stringstream ss(s);
+ string item;
+ while (getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+ }
+
+ std::string constant_name(std::string name);
+
+private:
+ ofstream_with_content_based_conditional_update f_service_;
+
+ std::string library_name_;
+ std::string library_prefix_;
+ std::string package_prefix_;
+ std::string pubspec_lib_;
+
+ std::string base_dir_;
+ std::string src_dir_;
+ std::string library_exports_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_dart_generator::init_generator() {
+ MKDIR(get_out_dir().c_str());
+
+ if (library_name_.empty()) {
+ library_name_ = find_library_name(program_);
+ }
+
+ string subdir = get_out_dir() + "/" + library_name_;
+ MKDIR(subdir.c_str());
+ base_dir_ = subdir;
+
+ if (library_prefix_.empty()) {
+ subdir += "/lib";
+ MKDIR(subdir.c_str());
+ subdir += "/src";
+ MKDIR(subdir.c_str());
+ src_dir_ = subdir;
+ } else {
+ src_dir_ = base_dir_;
+ }
+}
+
+string t_dart_generator::find_library_name(t_program* program) {
+ string name = program->get_namespace("dart");
+ if (name.empty()) {
+ name = program->get_name();
+ }
+ name = replace_all(name, ".", "_");
+ name = replace_all(name, "-", "_");
+ return name;
+}
+
+/**
+ * The Dart library
+ *
+ * @return String of the library, e.g. "library myservice;"
+ */
+string t_dart_generator::dart_library(string file_name) {
+ string out = "library " + library_prefix_ + library_name_;
+ if (!file_name.empty()) {
+ if (library_prefix_.empty()) {
+ out += ".src." + file_name;
+ } else {
+ out += "." + file_name;
+ }
+ }
+ return out + ";\n";
+}
+
+/**
+ * Prints imports for services
+ *
+ * @return List of imports for services
+ */
+string t_dart_generator::service_imports() {
+ return "import 'dart:async';" + endl;
+}
+
+/**
+ * Prints standard dart imports
+ *
+ * @return List of imports necessary for thrift
+ */
+string t_dart_generator::dart_thrift_imports() {
+ string imports = "import 'dart:typed_data' show Uint8List;" + endl +
+ "import 'package:thrift/thrift.dart';" + endl;
+
+ // add import for this library
+ if (package_prefix_.empty()) {
+ imports += "import 'package:" + library_name_ + "/" + library_name_ + ".dart';" + endl;
+ } else {
+ imports += "import 'package:" + package_prefix_ + library_name_ + ".dart';" + endl;
+ }
+
+ // add imports for included thrift files
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ string include_name = find_library_name(include);
+ string named_import = "t_" + include_name;
+ if (package_prefix_.empty()) {
+ imports += "import 'package:" + include_name + "/" + include_name + ".dart' as " + named_import + ";" + endl;
+ } else {
+ imports += "import 'package:" + package_prefix_ + include_name + ".dart' as " + named_import + ";" + endl;
+ }
+ }
+
+ return imports;
+}
+
+/**
+ * Not used
+ */
+void t_dart_generator::close_generator() {
+ generate_dart_library();
+
+ if (library_prefix_.empty()) {
+ generate_dart_pubspec();
+ }
+}
+
+void t_dart_generator::generate_dart_library() {
+ string f_library_name;
+ if (library_prefix_.empty()) {
+ f_library_name = base_dir_ + "/lib/" + library_name_ + ".dart";
+ } else {
+ f_library_name = get_out_dir() + "/" + library_name_ + ".dart";
+ }
+
+ ofstream_with_content_based_conditional_update f_library;
+ f_library.open(f_library_name.c_str());
+
+ f_library << autogen_comment() << endl;
+ f_library << "library " << library_prefix_ << library_name_ << ";" << endl2;
+ f_library << library_exports_;
+
+ f_library.close();
+}
+
+void t_dart_generator::export_class_to_library(string file_name, string class_name) {
+ string subdir;
+ if (library_prefix_.empty()) {
+ subdir = "src";
+ } else {
+ subdir = library_name_;
+ }
+ library_exports_ += "export '" + subdir + "/" + file_name + ".dart' show " + class_name + ";" + endl;
+}
+
+void t_dart_generator::generate_dart_pubspec() {
+ string f_pubspec_name = base_dir_ + "/pubspec.yaml";
+ ofstream_with_content_based_conditional_update f_pubspec;
+ f_pubspec.open(f_pubspec_name.c_str());
+
+ indent(f_pubspec) << "name: " << library_name_ << endl;
+ indent(f_pubspec) << "version: 0.0.1" << endl;
+ indent(f_pubspec) << "description: Autogenerated by Thrift Compiler" << endl;
+ f_pubspec << endl;
+
+ indent(f_pubspec) << "environment:" << endl;
+ indent_up();
+ indent(f_pubspec) << "sdk: '>=1.24.3 <3.0.0'" << endl;
+ indent_down();
+ f_pubspec << endl;
+
+ indent(f_pubspec) << "dependencies:" << endl;
+ indent_up();
+
+ if (pubspec_lib_.empty()) {
+ // default to relative path within working directory, which works for tests
+ indent(f_pubspec) << "thrift: # ^" << dart_thrift_version << endl;
+ indent_up();
+ indent(f_pubspec) << "path: ../../../../lib/dart" << endl;
+ indent_down();
+ } else {
+ const vector<std::string> lines = split(pubspec_lib_, '|');
+ for (const auto & line : lines) {
+ indent(f_pubspec) << line << endl;
+ }
+ }
+
+ // add included thrift files as dependencies
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ string include_name = find_library_name(include);
+ indent(f_pubspec) << include_name << ":" << endl;
+ indent_up();
+ indent(f_pubspec) << "path: ../" << include_name << endl;
+ indent_down();
+ }
+
+ indent_down();
+ f_pubspec << endl;
+
+ f_pubspec.close();
+}
+
+/**
+ * Not used
+ *
+ * @param ttypedef The type definition
+ */
+void t_dart_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Enums are a class with a set of static constants.
+ *
+ * @param tenum The enumeration
+ */
+void t_dart_generator::generate_enum(t_enum* tenum) {
+ // Make output file
+ string file_name = get_file_name(tenum->get_name());
+
+ string f_enum_name = src_dir_ + "/" + file_name + ".dart";
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ // Comment and add library
+ f_enum << autogen_comment() << dart_library(file_name) << endl;
+
+ string class_name = tenum->get_name();
+ export_class_to_library(file_name, class_name);
+ f_enum << "class " << class_name;
+ scope_up(f_enum);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << "static const int " << (*c_iter)->get_name() << " = " << value << ";"
+ << endl;
+ }
+
+ // Create a static Set with all valid values for this enum
+ f_enum << endl;
+
+ indent(f_enum) << "static final Set<int> VALID_VALUES = new Set.from([" << endl;
+ indent_up();
+ bool firstValue = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ // populate set
+ indent(f_enum) << (firstValue ? "" : ", ");
+ f_enum << (*c_iter)->get_name() << endl;
+ firstValue = false;
+ }
+ indent_down();
+ indent(f_enum) << "]);" << endl;
+
+ indent(f_enum) << "static final Map<int, String> VALUES_TO_NAMES = {" << endl;
+ indent_up();
+ firstValue = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ indent(f_enum) << (firstValue ? "" : ", ");
+ f_enum << (*c_iter)->get_name() << ": '" << (*c_iter)->get_name() << "'" << endl;
+ firstValue = false;
+ }
+ indent_down();
+ indent(f_enum) << "};" << endl;
+
+ scope_down(f_enum); // end class
+
+ f_enum.close();
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_dart_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+
+ string class_name = get_constants_class_name(program_name_);
+ string file_name = get_file_name(class_name);
+
+ string f_consts_name = src_dir_ + "/" + file_name + ".dart";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ // Print header
+ f_consts << autogen_comment() << dart_library(file_name) << endl;
+ f_consts << dart_thrift_imports() << endl;
+
+ export_class_to_library(file_name, class_name);
+ indent(f_consts) << "class " << class_name;
+ scope_up(f_consts);
+
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ print_const_value(f_consts,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ false);
+ f_consts << endl;
+ }
+
+ scope_down(f_consts);
+
+ f_consts.close();
+}
+
+void t_dart_generator::print_const_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval) {
+ type = get_true_type(type);
+
+ indent(out);
+ if (!defval) {
+ out << (in_static ? "var " : "static final ");
+ }
+ if (type->is_base_type()) {
+ if (!defval) {
+ out << type_name(type) << " ";
+ }
+ string v2 = render_const_value(out, name, type, value);
+ out << name;
+ out << " = " << v2 << ";" << endl << endl;
+ } else if (type->is_enum()) {
+ if (!defval) {
+ out << type_name(type) << " ";
+ }
+ out << name;
+ out << " = " << value->get_integer() << ";" << endl << endl;
+ } else if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ out << type_name(type) << " " << name << " = new " << type_name(type) << "()";
+ indent_up();
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ out << endl;
+ indent(out) << ".." << v_iter->first->get_string() << " = " << val;
+ }
+ indent_down();
+ out << ";" << endl;
+ } else if (type->is_map()) {
+ if (!defval) {
+ out << type_name(type) << " ";
+ }
+ out << name << " =";
+ scope_up(out);
+
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ indent(out) << key << ": " << val << "," << endl;
+ }
+ scope_down(out, ";" + endl);
+
+ out << endl;
+ } else if (type->is_list() || type->is_set()) {
+ if (!defval) {
+ out << type_name(type) << " ";
+ }
+ out << name << " = ";
+ t_type* etype;
+ if (type->is_list()) {
+ out << "[" << endl;
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ out << "new " << type_name(type) << ".from([" << endl;
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+
+ indent_up();
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ indent(out) << val << "," << endl;
+ }
+ indent_down();
+
+ if (type->is_list()) {
+ indent(out) << "];" << endl;
+ } else {
+ indent(out) << "]);" << endl;
+ }
+
+ } else {
+ throw "compiler error: no const of type " + type->get_name();
+ }
+}
+
+string t_dart_generator::render_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+ type = get_true_type(type);
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << "'" << get_escaped_string(value) << "'";
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << value->get_integer();
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << value->get_integer();
+ } else {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true);
+ out << endl;
+ render << t;
+ }
+
+ return render.str();
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is a class
+ * with data members, read(), write(), and an inner Isset class.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_struct(t_struct* tstruct) {
+ generate_dart_struct(tstruct, false);
+}
+
+/**
+ * Exceptions are structs, but they inherit from Exception
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_xception(t_struct* txception) {
+ generate_dart_struct(txception, true);
+}
+
+/**
+ * Dart struct definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct(t_struct* tstruct, bool is_exception) {
+ string file_name = get_file_name(tstruct->get_name());
+ string f_struct_name = src_dir_ + "/" + file_name + ".dart";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << dart_library(file_name) << endl;
+
+ string imports;
+
+ f_struct << dart_thrift_imports() << endl;
+
+ generate_dart_struct_definition(f_struct, tstruct, is_exception, false, file_name);
+
+ f_struct.close();
+}
+
+/**
+ * Dart struct definition. This has various parameters, as it could be
+ * generated standalone or inside another class as a helper. If it
+ * is a helper than it is a static class.
+ *
+ * @param tstruct The struct definition
+ * @param is_exception Is this an exception?
+ * @param in_class If inside a class, needs to be static class
+ * @param is_result If this is a result it needs a different writer
+ */
+void t_dart_generator::generate_dart_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result,
+ string export_file_name) {
+ generate_dart_doc(out, tstruct);
+
+ string class_name = tstruct->get_name();
+ if (!export_file_name.empty()) {
+ export_class_to_library(export_file_name, class_name);
+ }
+ indent(out) << "class " << class_name << " ";
+
+ out << "implements TBase";
+ if (is_exception) {
+ out << ", Exception ";
+ }
+ scope_up(out);
+
+ indent(out) << "static final TStruct _STRUCT_DESC = new TStruct(\"" << class_name
+ << "\");" << endl;
+
+ // Members are public for -dart, private for -dartbean
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "static final TField _" << constant_name((*m_iter)->get_name())
+ << "_FIELD_DESC = new TField(\"" << (*m_iter)->get_name() << "\", "
+ << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << ");"
+ << endl;
+ }
+
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_dart_doc(out, *m_iter);
+ indent(out) << type_name((*m_iter)->get_type()) + " _"
+ << get_member_name((*m_iter)->get_name()) << init_value(*m_iter) << ";" << endl;
+
+ indent(out) << "static const int " << upcase_string((*m_iter)->get_name())
+ << " = " << (*m_iter)->get_key() << ";" << endl;
+ }
+
+ out << endl;
+
+ // Inner Isset class
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!type_can_be_null((*m_iter)->get_type())) {
+ string field_name = get_member_name((*m_iter)->get_name());
+ indent(out) << "bool __isset_" << field_name << " = false;" << endl;
+ }
+ }
+ }
+
+ out << endl;
+
+ // Default constructor
+ indent(out) << tstruct->get_name() << "()";
+ scope_up(out);
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL) {
+ print_const_value(out,
+ "this." + get_member_name((*m_iter)->get_name()),
+ t,
+ (*m_iter)->get_value(),
+ true,
+ true);
+ }
+ }
+ scope_down(out);
+ out << endl;
+
+ generate_dart_bean_boilerplate(out, tstruct);
+ generate_generic_field_getters(out, tstruct);
+ generate_generic_field_setters(out, tstruct);
+ generate_generic_isset_method(out, tstruct);
+
+ generate_dart_struct_reader(out, tstruct);
+ if (is_result) {
+ generate_dart_struct_result_writer(out, tstruct);
+ } else {
+ generate_dart_struct_writer(out, tstruct);
+ }
+ generate_dart_struct_tostring(out, tstruct);
+ generate_dart_validator(out, tstruct);
+ scope_down(out);
+ out << endl;
+}
+
+/**
+ * Generates a function to read all the fields of the struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_reader(ostream& out, t_struct* tstruct) {
+ indent(out) << "read(TProtocol iprot)";
+ scope_up(out);
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Declare stack tmp variables and read struct header
+ indent(out) << "TField field;" << endl;
+ indent(out) << "iprot.readStructBegin();" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true)";
+ scope_up(out);
+
+ // Read beginning field marker
+ indent(out) << "field = iprot.readFieldBegin();" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if (field.type == TType.STOP)";
+ scope_up(out);
+ indent(out) << "break;" << endl;
+ scope_down(out);
+
+ // Switch statement on the field we are reading
+ indent(out) << "switch (field.id)";
+ scope_up(out);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << upcase_string((*f_iter)->get_name()) << ":" << endl;
+ indent_up();
+
+ indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ")";
+ scope_up(out);
+
+ generate_deserialize_field(out, *f_iter, "this.");
+ generate_isset_set(out, *f_iter);
+
+ scope_down(out, " else");
+ scope_up(out);
+ indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl;
+ scope_down(out);
+
+ indent(out) << "break;" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ indent(out) << "default:" << endl;
+ indent_up();
+ indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl;
+ indent(out) << "break;" << endl;
+ indent_down();
+
+ scope_down(out);
+
+ // Read field end marker
+ indent(out) << "iprot.readFieldEnd();" << endl;
+
+ scope_down(out);
+
+ indent(out) << "iprot.readStructEnd();" << endl2;
+
+ // in non-beans style, check for required fields of primitive type
+ // (which can be checked here but not in the general validate method)
+ indent(out) << "// check for required fields of primitive type, which can't be "
+ "checked in the validate method" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) {
+ string field_name = get_member_name((*f_iter)->get_name());
+ indent(out) << "if (!__isset_" << field_name << ")";
+ scope_up(out);
+ indent(out) << " throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"Required field '"
+ << field_name
+ << "' was not found in serialized data! Struct: \" + toString());" << endl;
+ scope_down(out, endl2);
+ }
+ }
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl;
+
+ scope_down(out, endl2);
+}
+
+// generates dart method to perform various checks
+// (e.g. check that all required fields are set)
+void t_dart_generator::generate_dart_validator(ostream& out, t_struct* tstruct) {
+ indent(out) << "validate()";
+ scope_up(out);
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "// check for required fields" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ string field_name = get_member_name((*f_iter)->get_name());
+ if (type_can_be_null((*f_iter)->get_type())) {
+ indent(out) << "if (" << field_name << " == null)";
+ scope_up(out);
+ indent(out) << "throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"Required field '"
+ << field_name << "' was not present! Struct: \" + toString());"
+ << endl;
+ scope_down(out);
+ } else {
+ indent(out) << "// alas, we cannot check '" << field_name
+ << "' because it's a primitive and you chose the non-beans generator." << endl;
+ }
+ }
+ }
+
+ // check that fields of type enum have valid values
+ indent(out) << "// check that fields of type enum have valid values" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ t_type* type = field->get_type();
+ // if field is an enum, check that its value is valid
+ if (type->is_enum()) {
+ string field_name = get_member_name(field->get_name());
+ indent(out) << "if (" << generate_isset_check(field) << " && !" << get_ttype_class_name(type)
+ << ".VALID_VALUES.contains(" << field_name << "))";
+ scope_up(out);
+ indent(out) << "throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"The field '"
+ << field_name << "' has been assigned the invalid value "
+ << "$" << field_name << "\");" << endl;
+ scope_down(out);
+ }
+ }
+
+ scope_down(out, endl2);
+}
+
+/**
+ * Generates a function to write all the fields of the struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "write(TProtocol oprot)";
+ scope_up(out);
+
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl2;
+
+ indent(out) << "oprot.writeStructBegin(_STRUCT_DESC);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ string field_name = get_member_name((*f_iter)->get_name());
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ")";
+ scope_up(out);
+ }
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ indent(out) << "if (this." << field_name << " != null)";
+ scope_up(out);
+ }
+
+ indent(out) << "oprot.writeFieldBegin(_" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ if (null_allowed) {
+ scope_down(out);
+ }
+ if (could_be_unset) {
+ scope_down(out);
+ }
+ }
+ // Write the struct map
+ indent(out) << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();"
+ << endl;
+
+ scope_down(out, endl2);
+}
+
+/**
+ * Generates a function to write all the fields of the struct,
+ * which is a function result. These fields are only written
+ * if they are set in the Isset array, and only one of them
+ * can be set at a time.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_result_writer(ostream& out, t_struct* tstruct) {
+ indent(out) << "write(TProtocol oprot)";
+ scope_up(out);
+
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "oprot.writeStructBegin(_STRUCT_DESC);" << endl2;
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ indent(out) << "if ";
+ } else {
+ out << " else if ";
+ }
+
+ out << "(this." << generate_isset_check(*f_iter) << ")";
+ scope_up(out);
+
+ indent(out) << "oprot.writeFieldBegin(_" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ scope_down(out, "");
+ }
+ out << endl;
+
+ // Write the struct map
+ indent(out) << "oprot.writeFieldStop();" << endl << indent()
+ << "oprot.writeStructEnd();" << endl;
+
+ scope_down(out, endl2);
+}
+
+void t_dart_generator::generate_generic_field_getters(std::ostream& out,
+ t_struct* tstruct) {
+ // create the getter
+ indent(out) << "getFieldValue(int fieldID)";
+ scope_up(out);
+
+ indent(out) << "switch (fieldID)";
+ scope_up(out);
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ std::string field_name = get_member_name(field->get_name());
+
+ indent(out) << "case " << upcase_string(field_name) << ":" << endl;
+ indent_up();
+ indent(out) << "return this." << field_name << ";" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent_up();
+ indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl;
+ indent_down();
+
+ scope_down(out); // switch
+ scope_down(out, endl2); // method
+}
+
+void t_dart_generator::generate_generic_field_setters(std::ostream& out,
+ t_struct* tstruct) {
+
+ // create the setter
+ indent(out) << "setFieldValue(int fieldID, Object value)";
+ scope_up(out);
+
+ indent(out) << "switch (fieldID)";
+ scope_up(out);
+
+ // build up the bodies of both the getter and setter at once
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ std::string field_name = get_member_name(field->get_name());
+
+ indent(out) << "case " << upcase_string(field_name) << ":" << endl;
+ indent_up();
+
+ indent(out) << "if (value == null)";
+ scope_up(out);
+ indent(out) << "unset" << get_cap_name(field_name) << "();" << endl;
+
+ scope_down(out, " else");
+ scope_up(out);
+ indent(out) << "this." << field_name << " = value;" << endl;
+ scope_down(out);
+
+ indent(out) << "break;" << endl;
+
+ indent_down();
+ out << endl;
+ }
+
+ indent(out) << "default:" << endl;
+ indent_up();
+ indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl;
+ indent_down();
+
+ scope_down(out); // switch
+ scope_down(out, endl2); // method
+}
+
+// Creates a generic isSet method that takes the field number as argument
+void t_dart_generator::generate_generic_isset_method(std::ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // create the isSet method
+ indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a "
+ "value) and false otherwise" << endl;
+ indent(out) << "bool isSet(int fieldID)";
+ scope_up(out);
+
+ indent(out) << "switch (fieldID)";
+ scope_up(out);
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ indent(out) << "case " << upcase_string(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << "return " << generate_isset_check(field) << ";" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent_up();
+ indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl;
+ indent_down();
+
+ scope_down(out); // switch
+ scope_down(out, endl2); // method
+}
+
+/**
+ * Generates a set of Dart Bean boilerplate functions (setters, getters, etc.)
+ * for the given struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_bean_boilerplate(ostream& out,
+ t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = get_member_name(field->get_name());
+ std::string cap_name = get_cap_name(field_name);
+
+ indent(out) << "// " << field_name << endl;
+
+ // Simple getter
+ generate_dart_doc(out, field);
+ indent(out) << type_name(type) << " get " << field_name << " => this._" << field_name << ";" << endl2;
+
+ // Simple setter
+ generate_dart_doc(out, field);
+ indent(out) << "set " << field_name << "(" << type_name(type) << " " << field_name << ")";
+ scope_up(out);
+ indent(out) << "this._" << field_name << " = " << field_name << ";" << endl;
+ generate_isset_set(out, field);
+ scope_down(out, endl2);
+
+ // isSet method
+ indent(out) << "bool is" << get_cap_name("set") << cap_name << "()";
+ if (type_can_be_null(type)) {
+ out << " => this." << field_name << " != null;" << endl2;
+ } else {
+ out << " => this.__isset_" << field_name << ";" << endl2;
+ }
+
+ // Unsetter
+ indent(out) << "unset" << cap_name << "()";
+ scope_up(out);
+ if (type_can_be_null(type)) {
+ indent(out) << "this." << field_name << " = null;" << endl;
+ } else {
+ indent(out) << "this.__isset_" << field_name << " = false;" << endl;
+ }
+ scope_down(out, endl2);
+ }
+}
+
+/**
+ * Generates a toString() method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_dart_generator::generate_dart_struct_tostring(ostream& out,
+ t_struct* tstruct) {
+ indent(out) << "String toString()";
+ scope_up(out);
+
+ indent(out) << "StringBuffer ret = new StringBuffer(\""
+ << tstruct->get_name() << "(\");" << endl2;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ")";
+ scope_up(out);
+ }
+
+ t_field* field = (*f_iter);
+ std::string field_name = get_member_name(field->get_name());
+
+ if (!first) {
+ indent(out) << "ret.write(\", \");" << endl;
+ }
+ indent(out) << "ret.write(\"" << field_name << ":\");" << endl;
+ bool can_be_null = type_can_be_null(field->get_type());
+ if (can_be_null) {
+ indent(out) << "if (this." << field_name << " == null)";
+ scope_up(out);
+ indent(out) << "ret.write(\"null\");" << endl;
+ scope_down(out, " else");
+ scope_up(out);
+ }
+
+ if (field->get_type()->is_binary()) {
+ indent(out) << "ret.write(\"BINARY\");" << endl;
+ } else if (field->get_type()->is_enum()) {
+ indent(out) << "String " << field_name << "_name = "
+ << get_ttype_class_name(field->get_type())
+ << ".VALUES_TO_NAMES[this." << field_name << "];" << endl;
+ indent(out) << "if (" << field_name << "_name != null)";
+ scope_up(out);
+ indent(out) << "ret.write(" << field_name << "_name);" << endl;
+ indent(out) << "ret.write(\" (\");" << endl;
+ scope_down(out);
+ indent(out) << "ret.write(this." << field_name << ");" << endl;
+ indent(out) << "if (" << field_name << "_name != null)";
+ scope_up(out);
+ indent(out) << "ret.write(\")\");" << endl;
+ scope_down(out);
+ } else {
+ indent(out) << "ret.write(this." << field_name << ");" << endl;
+ }
+
+ if (can_be_null) {
+ scope_down(out);
+ }
+ if (could_be_unset) {
+ scope_down(out);
+ }
+
+ out << endl;
+ first = false;
+ }
+
+ indent(out) << "ret.write(\")\");" << endl2;
+
+ indent(out) << "return ret.toString();" << endl;
+
+ scope_down(out, endl2);
+}
+
+/**
+ * Returns a string with the dart representation of the given thrift type
+ * (e.g. for the type struct it returns "TType.STRUCT")
+ */
+std::string t_dart_generator::get_dart_type_string(t_type* type) {
+ if (type->is_list()) {
+ return "TType.LIST";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_typedef()) {
+ return get_dart_type_string(((t_typedef*)type)->get_type());
+ } else if (type->is_base_type()) {
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "TType.VOID";
+ break;
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ break;
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ break;
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ break;
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ break;
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ break;
+ default:
+ throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_dart_generator::get_dart_type_string!");
+ break; // This should never happen!
+ }
+ } else {
+ throw std::runtime_error(
+ "Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_dart_generator::get_dart_type_string!"); // This should never happen!
+ }
+}
+
+void t_dart_generator::generate_service(t_service* tservice) {
+ string file_name = get_file_name(service_name_);
+ string f_service_name = src_dir_ + "/" + file_name + ".dart";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << dart_library(file_name) << endl;
+ f_service_ << service_imports() << dart_thrift_imports() << endl;
+ f_service_ << endl;
+
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+
+ f_service_.close();
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_dart_generator::generate_service_interface(t_service* tservice) {
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends_iface = " extends " + get_ttype_class_name(tservice->get_extends());
+ }
+
+ generate_dart_doc(f_service_, tservice);
+
+ string class_name = service_name_;
+ export_class_to_library(get_file_name(service_name_), class_name);
+ indent(f_service_) << "abstract class " << class_name << extends_iface;
+ scope_up(f_service_);
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << endl;
+ generate_dart_doc(f_service_, *f_iter);
+ indent(f_service_) << function_signature(*f_iter) << ";" << endl;
+ }
+
+ scope_down(f_service_, endl2);
+}
+
+/**
+ * Generates structs for all the service args and return types
+ *
+ * @param tservice The service
+ */
+void t_dart_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_dart_struct_definition(f_service_, ts, false, false);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_dart_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = get_ttype_class_name(tservice->get_extends());
+ extends_client = " extends " + extends + "Client";
+ }
+
+ string class_name = service_name_ + "Client";
+ export_class_to_library(get_file_name(service_name_), class_name);
+ indent(f_service_) << "class " << class_name << extends_client
+ << " implements " << service_name_;
+ scope_up(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << class_name << "(TProtocol iprot, [TProtocol oprot = null])";
+
+ if (!extends.empty()) {
+ indent_up();
+ f_service_ << endl;
+ indent(f_service_) << ": super(iprot, oprot);" << endl;
+ indent_down();
+ } else {
+ scope_up(f_service_);
+ indent(f_service_) << "_iprot = iprot;" << endl;
+ indent(f_service_) << "_oprot = (oprot == null) ? iprot : oprot;" << endl;
+ scope_down(f_service_);
+ }
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ indent(f_service_) << "TProtocol _iprot;" << endl2;
+ indent(f_service_) << "TProtocol get iprot => _iprot;" << endl2;
+ indent(f_service_) << "TProtocol _oprot;" << endl2;
+ indent(f_service_) << "TProtocol get oprot => _oprot;" << endl2;
+ indent(f_service_) << "int _seqid = 0;" << endl2;
+ indent(f_service_) << "int get seqid => _seqid;" << endl2;
+ indent(f_service_) << "int nextSeqid() => ++_seqid;" << endl2;
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ // Open function
+ indent(f_service_) << function_signature(*f_iter) << " async";
+ scope_up(f_service_);
+
+ // Get the struct of function call params
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ string argsname = get_args_class_name((*f_iter)->get_name());
+ vector<t_field*>::const_iterator fld_iter;
+ const vector<t_field*>& fields = arg_struct->get_members();
+
+ // Serialize the request
+ indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << (*f_iter)->get_name() << "\", "
+ << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL")
+ << ", nextSeqid()));" << endl;
+ indent(f_service_) << argsname << " args = new " << argsname << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ string arg_field_name = get_member_name((*fld_iter)->get_name());
+ indent(f_service_) << "args." << arg_field_name << " = "
+ << arg_field_name << ";" << endl;
+ }
+
+ indent(f_service_) << "args.write(oprot);" << endl;
+ indent(f_service_) << "oprot.writeMessageEnd();" << endl2;
+
+ indent(f_service_) << "await oprot.transport.flush();" << endl2;
+
+ if (!(*f_iter)->is_oneway()) {
+ indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << endl;
+ indent(f_service_) << "if (msg.type == TMessageType.EXCEPTION)";
+ scope_up(f_service_);
+ indent(f_service_) << "TApplicationError error = TApplicationError.read(iprot);" << endl;
+ indent(f_service_) << "iprot.readMessageEnd();" << endl;
+ indent(f_service_) << "throw error;" << endl;
+ scope_down(f_service_, endl2);
+
+ string result_class = get_result_class_name((*f_iter)->get_name());
+ indent(f_service_) << result_class << " result = new " << result_class << "();" << endl;
+ indent(f_service_) << "result.read(iprot);" << endl;
+ indent(f_service_) << "iprot.readMessageEnd();" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "if (result." << generate_isset_check("success") << ")";
+ scope_up(f_service_);
+ indent(f_service_) << "return result.success;" << endl;
+ scope_down(f_service_, endl2);
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ string result_field_name = get_member_name((*x_iter)->get_name());
+ indent(f_service_) << "if (result." << result_field_name << " != null)";
+ scope_up(f_service_);
+ indent(f_service_) << "throw result." << result_field_name << ";" << endl;
+ scope_down(f_service_);
+ }
+
+ // If you get here it's an exception, unless a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "return;" << endl;
+ } else {
+ indent(f_service_) << "throw new TApplicationError(TApplicationErrorType.MISSING_RESULT, \""
+ << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
+ }
+ }
+
+ scope_down(f_service_, endl2);
+ }
+
+ scope_down(f_service_, endl2);
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_dart_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // typedef
+ indent(f_service_) << "typedef void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" << endl2;
+
+ // Extends stuff
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = get_ttype_class_name(tservice->get_extends());
+ extends_processor = " extends " + extends + "Processor";
+ }
+
+ // Generate the header portion
+ string class_name = service_name_ + "Processor";
+ export_class_to_library(get_file_name(service_name_), class_name);
+ indent(f_service_) << "class " << class_name << extends_processor << " implements TProcessor";
+ scope_up(f_service_);
+
+ indent(f_service_) << class_name << "(" << service_name_ << " iface)";
+ if (!extends.empty()) {
+ indent_up();
+ f_service_ << endl;
+ indent(f_service_) << ": super(iface)";
+ indent_down();
+ }
+ scope_up(f_service_);
+
+ if (extends.empty()) {
+ indent(f_service_) << "iface_ = iface;" << endl;
+ }
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_service_) << "PROCESS_MAP[\"" << (*f_iter)->get_name()
+ << "\"] = " << get_member_name((*f_iter)->get_name()) << ";" << endl;
+ }
+ scope_down(f_service_, endl2);
+
+ indent(f_service_) << service_name_ << " iface_;" << endl;
+
+ if (extends.empty()) {
+ indent(f_service_) << "final Map<String, ProcessFunction> PROCESS_MAP = {};" << endl;
+ }
+
+ f_service_ << endl;
+
+ // Generate the server implementation
+ indent(f_service_) << "bool process(TProtocol iprot, TProtocol oprot)";
+ scope_up(f_service_);
+ indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << endl;
+ indent(f_service_) << "ProcessFunction fn = PROCESS_MAP[msg.name];" << endl;
+ indent(f_service_) << "if (fn == null)";
+ scope_up(f_service_);
+ indent(f_service_) << "TProtocolUtil.skip(iprot, TType.STRUCT);" << endl;
+ indent(f_service_) << "iprot.readMessageEnd();" << endl;
+ indent(f_service_) << "TApplicationError x = new TApplicationError(TApplicationErrorType.UNKNOWN_METHOD, "
+ "\"Invalid method name: '\"+msg.name+\"'\");" << endl;
+ indent(f_service_) << "oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));" << endl;
+ indent(f_service_) << "x.write(oprot);" << endl;
+ indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+ indent(f_service_) << "oprot.transport.flush();" << endl;
+ indent(f_service_) << "return true;" << endl;
+ scope_down(f_service_);
+ indent(f_service_) << "fn(msg.seqid, iprot, oprot);" << endl;
+ indent(f_service_) << "return true;" << endl;
+ scope_down(f_service_, endl2); // process function
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ scope_down(f_service_, endl2); // class
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_dart_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ t_struct result(program_, get_result_class_name(tfunction->get_name()));
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_dart_struct_definition(f_service_, &result, false, true);
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_dart_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+
+ bool await_result = (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void());
+
+ indent(f_service_) << get_member_name(tfunction->get_name()) << "(int seqid, TProtocol iprot, TProtocol oprot)";
+ if (await_result) {
+ f_service_ << " async";
+ }
+ scope_up(f_service_);
+
+ string argsname = get_args_class_name(tfunction->get_name());
+ string resultname = get_result_class_name(tfunction->get_name());
+
+ indent(f_service_) << argsname << " args = new " << argsname << "();" << endl;
+ indent(f_service_) << "args.read(iprot);" << endl;
+ indent(f_service_) << "iprot.readMessageEnd();" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << resultname << " result = new " << resultname << "();" << endl;
+ }
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ indent(f_service_) << "try";
+ scope_up(f_service_);
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (await_result) {
+ f_service_ << "result.success = await ";
+ }
+ f_service_ << "iface_." << get_member_name(tfunction->get_name()) << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << get_member_name((*f_iter)->get_name());
+ }
+ f_service_ << ");" << endl;
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ string result_field_name = get_member_name((*x_iter)->get_name());
+ scope_down(f_service_, "");
+ f_service_ << " on " << type_name((*x_iter)->get_type())
+ << " catch(" << result_field_name << ")";
+ scope_up(f_service_);
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << "result." << result_field_name << " = "
+ << result_field_name << ";" << endl;
+ }
+ }
+ scope_down(f_service_, " ");
+ f_service_ << "catch (th)";
+ scope_up(f_service_);
+ indent(f_service_) << "// Internal error" << endl;
+ indent(f_service_) << "TApplicationError x = new "
+ "TApplicationError(TApplicationErrorType.INTERNAL_ERROR, \"Internal error processing "
+ << tfunction->get_name() << "\");" << endl;
+ indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.EXCEPTION, seqid));" << endl;
+ indent(f_service_) << "x.write(oprot);" << endl;
+ indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+ indent(f_service_) << "oprot.transport.flush();" << endl;
+ indent(f_service_) << "return;" << endl;
+ scope_down(f_service_);
+ }
+
+ if (tfunction->is_oneway()) {
+ indent(f_service_) << "return;" << endl;
+ } else {
+ indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.REPLY, seqid));" << endl;
+ indent(f_service_) << "result.write(oprot);" << endl;
+ indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+ indent(f_service_) << "oprot.transport.flush();" << endl;
+ }
+
+ scope_down(f_service_, endl2);
+}
+
+/**
+ * Deserializes a field of any type.
+ *
+ * @param tfield The field
+ * @param prefix The variable name or container for this field
+ */
+void t_dart_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+ string field_name = get_member_name(tfield->get_name());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + field_name;
+ }
+
+ string name = prefix + field_name;
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent(out) << name << " = iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "readBinary();";
+ } else {
+ out << "readString();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble();";
+ break;
+ default:
+ throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32();";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ field_name.c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, invokes read()
+ */
+void t_dart_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ indent(out) << prefix << " = new " << type_name(tstruct) << "();" << endl;
+ indent(out) << prefix << ".read(iprot);" << endl;
+}
+
+/**
+ * Deserializes a container by reading its size and then iterating
+ */
+void t_dart_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ indent(out);
+ scope_up(out, "");
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "TMap " << obj << " = iprot.readMapBegin();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "TSet " << obj << " = iprot.readSetBegin();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "TList " << obj << " = iprot.readListBegin();" << endl;
+ }
+
+ indent(out) << prefix << " = new " << type_name(ttype) << "();" << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".length"
+ << "; "
+ << "++" << i << ")";
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot.readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot.readListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_dart_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey) << endl;
+ indent(out) << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+/**
+ * Deserializes a set element
+ */
+void t_dart_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Deserializes a list element
+ */
+void t_dart_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_dart_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+ string field_name = get_member_name(tfield->get_name());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + field_name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + field_name);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + field_name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + field_name;
+ indent(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ");";
+ } else {
+ out << "writeString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ field_name.c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_dart_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << prefix << ".write(oprot);" << endl;
+}
+
+/**
+ * Serializes a container by writing its size then the elements.
+ *
+ * @param ttype The type of container
+ * @param prefix String prefix for fields
+ */
+void t_dart_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ indent(out);
+ scope_up(out, "");
+
+ if (ttype->is_map()) {
+ string iter = tmp("_key");
+ indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".length));"
+ << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", " << prefix << ".length));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListBegin(new TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));"
+ << endl;
+ }
+
+ string iter = tmp("elem");
+ if (ttype->is_map()) {
+ indent(out) << "for (var " << iter << " in " << prefix << ".keys)";
+ } else if (ttype->is_set() || ttype->is_list()) {
+ indent(out) << "for (var " << iter << " in " << prefix << ")";
+ }
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter, prefix);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ }
+
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ */
+void t_dart_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string iter,
+ string map) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "");
+ t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_dart_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_dart_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Returns a Dart type name
+ *
+ * @param ttype The type
+ * @return Dart type name, i.e. Map<Key, Value>
+ */
+string t_dart_generator::type_name(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype);
+ } else if (ttype->is_enum()) {
+ return "int";
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ return "Map<" + type_name(tmap->get_key_type()) + ", "
+ + type_name(tmap->get_val_type()) + ">";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ return "Set<" + type_name(tset->get_elem_type()) + ">";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ return "List<" + type_name(tlist->get_elem_type()) + ">";
+ }
+
+ return get_ttype_class_name(ttype);
+}
+
+/**
+ * Returns the Dart type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ */
+string t_dart_generator::base_type_name(t_base_type* type) {
+ t_base_type::t_base tbase = type->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ return "Uint8List";
+ } else {
+ return "String";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "int";
+ case t_base_type::TYPE_DOUBLE:
+ return "double";
+ default:
+ throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_dart_generator::declare_field(t_field* tfield, bool init) {
+ string field_name = get_member_name(tfield->get_name());
+ string result = type_name(tfield->get_type()) + " " + field_name;
+ if (init) {
+ t_type* ttype = get_true_type(tfield->get_type());
+ if (ttype->is_base_type() && tfield->get_value() != NULL) {
+ std:: ofstream dummy;
+ result += " = " + render_const_value(dummy, field_name, ttype, tfield->get_value());
+ } else if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = 0.0";
+ break;
+ }
+
+ } else if (ttype->is_enum()) {
+ result += " = 0";
+ } else if (ttype->is_container()) {
+ result += " = new " + type_name(ttype) + "()";
+ } else {
+ result += " = new " + type_name(ttype) + "()";
+ ;
+ }
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_dart_generator::function_signature(t_function* tfunction) {
+ std::string arguments = argument_list(tfunction->get_arglist());
+
+ std::string returntype;
+ if (tfunction->get_returntype()->is_void()) {
+ returntype = "Future";
+ } else {
+ returntype = "Future<" + type_name(tfunction->get_returntype()) + ">";
+ }
+
+ std::string result = returntype + " " + get_member_name(tfunction->get_name()) +
+ "(" + arguments + ")";
+ return result;
+}
+
+/**
+ * Renders a comma separated field list, with type names
+ */
+string t_dart_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ string field_name = get_member_name((*f_iter)->get_name());
+ result += type_name((*f_iter)->get_type()) + " " + field_name;
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_dart_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+std::string t_dart_generator::init_value(t_field* field) {
+ // Do not initialize optional fields
+ if (field->get_req() == t_field::T_OPTIONAL) {
+ return "";
+ }
+
+ t_type* ttype = field->get_type();
+
+ // Get the actual type for a typedef
+ if (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ // Only consider base types for default initialization
+ if (!ttype->is_base_type()) {
+ return "";
+ }
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+
+ // Initialize bools, ints, and doubles with sane defaults
+ string result;
+ switch (tbase) {
+ case t_base_type::TYPE_BOOL:
+ result = " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result = " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result = " = 0.0";
+ break;
+ case t_base_type::TYPE_VOID:
+ case t_base_type::TYPE_STRING:
+ result = "";
+ break;
+ }
+
+ return result;
+}
+
+std::string t_dart_generator::get_cap_name(std::string name) {
+ name[0] = toupper(name[0]);
+ return name;
+}
+
+std::string t_dart_generator::get_member_name(std::string name) {
+ name[0] = tolower(name[0]);
+ return name;
+}
+
+std::string t_dart_generator::get_args_class_name(std::string name) {
+ return name + "_args";
+}
+
+std::string t_dart_generator::get_result_class_name(std::string name) {
+ return name + "_result";
+}
+
+std::string t_dart_generator::get_file_name(std::string name) {
+ // e.g. change APIForFileIO to api_for_file_io
+
+ string ret;
+ const char* tmp = name.c_str();
+ bool is_prev_lc = true;
+ bool is_current_lc = tmp[0] == tolower(tmp[0]);
+ bool is_next_lc = false;
+
+ for (unsigned int i = 0; i < name.length(); i++) {
+ char lc = tolower(tmp[i]);
+
+ if (i == name.length() - 1) {
+ is_next_lc = false;
+ } else {
+ is_next_lc = (tmp[i+1] == tolower(tmp[i+1]));
+ }
+
+ if (i != 0 && !is_current_lc && (is_prev_lc || is_next_lc)) {
+ ret += "_";
+ }
+ ret += lc;
+
+ is_prev_lc = is_current_lc;
+ is_current_lc = is_next_lc;
+ }
+
+ return ret;
+}
+
+std::string t_dart_generator::get_constants_class_name(std::string name) {
+ // e.g. change my_great_model to MyGreatModelConstants
+ string ret;
+ const char* tmp = name.c_str();
+ bool is_prev_underscore = true;
+
+ for (unsigned int i = 0; i < name.length(); i++) {
+ if (tmp[i] == '_') {
+ is_prev_underscore = true;
+ } else {
+ if (is_prev_underscore) {
+ ret += toupper(tmp[i]);
+ } else {
+ ret += tmp[i];
+ }
+
+ is_prev_underscore = false;
+ }
+ }
+
+ return ret + "Constants";
+}
+
+string t_dart_generator::constant_name(string name) {
+ string constant_name;
+
+ bool is_first = true;
+ bool was_previous_char_upper = false;
+ for (char character : name) {
+ bool is_upper = isupper(character);
+
+ if (is_upper && !is_first && !was_previous_char_upper) {
+ constant_name += '_';
+ }
+ constant_name += toupper(character);
+
+ is_first = false;
+ was_previous_char_upper = is_upper;
+ }
+
+ return constant_name;
+}
+
+/**
+ * Emits a doc comment if the provided object has a doc in Thrift
+ */
+void t_dart_generator::generate_dart_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_docstring_comment(out, "", "/// ", tdoc->get_doc(), "");
+ }
+}
+
+/**
+ * Emits a doc comment if the provided function object has a doc in Thrift
+ */
+void t_dart_generator::generate_dart_doc(ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc()) {
+ stringstream ss;
+ ss << tfunction->get_doc();
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ string field_name = get_member_name(p->get_name());
+ ss << "\n@param " << field_name;
+ if (p->has_doc()) {
+ ss << " " << p->get_doc();
+ }
+ }
+ generate_docstring_comment(out, "", "/// ", ss.str(), "");
+ }
+}
+
+std::string t_dart_generator::generate_isset_check(t_field* field) {
+ string field_name = get_member_name(field->get_name());
+ return generate_isset_check(field_name);
+}
+
+std::string t_dart_generator::generate_isset_check(std::string field_name) {
+ return "is" + get_cap_name("set") + get_cap_name(field_name) + "()";
+}
+
+void t_dart_generator::generate_isset_set(ostream& out, t_field* field) {
+ if (!type_can_be_null(field->get_type())) {
+ string field_name = get_member_name(field->get_name());
+ indent(out) << "this.__isset_" << field_name << " = true;" << endl;
+ }
+}
+
+std::string t_dart_generator::get_ttype_class_name(t_type* ttype) {
+ if (program_ == ttype->get_program()) {
+ return ttype->get_name();
+ } else {
+ string named_import = "t_" + find_library_name(ttype->get_program());
+ return named_import + "." + ttype->get_name();
+ }
+}
+
+THRIFT_REGISTER_GENERATOR(
+ dart,
+ "Dart",
+ " library_name: Optional override for library name.\n"
+ " library_prefix: Generate code that can be used within an existing library.\n"
+ " Use a dot-separated string, e.g. \"my_parent_lib.src.gen\"\n"
+ " pubspec_lib: Optional override for thrift lib dependency in pubspec.yaml,\n"
+ " e.g. \"thrift: 0.x.x\". Use a pipe delimiter to separate lines,\n"
+ " e.g. \"thrift:| git:| url: git@foo.com\"\n"
+)
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc
new file mode 100644
index 000000000..4a2ebdaaf
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc
@@ -0,0 +1,4011 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <list>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include <cctype>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+class t_delphi_generator : public t_oop_generator {
+public:
+ t_delphi_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ indent_impl_ = 0;
+ has_forward = false;
+ has_enum = false;
+ has_const = false;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ ansistr_binary_ = false;
+ register_types_ = false;
+ constprefix_ = false;
+ events_ = false;
+ xmldoc_ = false;
+ async_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("ansistr_binary") == 0) {
+ ansistr_binary_ = true;
+ } else if( iter->first.compare("register_types") == 0) {
+ register_types_ = true;
+ } else if( iter->first.compare("constprefix") == 0) {
+ constprefix_ = true;
+ } else if( iter->first.compare("events") == 0) {
+ events_ = true;
+ } else if( iter->first.compare("xmldoc") == 0) {
+ xmldoc_ = true;
+ } else if( iter->first.compare("async") == 0) {
+ async_ = true;
+ } else {
+ throw "unknown option delphi:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-delphi";
+ escape_.clear();
+ escape_['\''] = "''";
+ }
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_forward_declaration(t_struct* tstruct) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+ void generate_property(ostream& out, t_field* tfield, bool isPublic, bool is_xception);
+ void generate_property_writer_(ostream& out, t_field* tfield, bool isPublic);
+
+ void generate_delphi_property(ostream& out,
+ bool struct_is_exception,
+ t_field* tfield,
+ bool isPublic,
+ std::string fieldPrefix = "");
+ void generate_delphi_isset_reader_definition(ostream& out, t_field* tfield, bool is_xception);
+ void generate_delphi_property_reader_definition(ostream& out,
+ t_field* tfield,
+ bool is_xception_class);
+ void generate_delphi_property_writer_definition(ostream& out,
+ t_field* tfield,
+ bool is_xception_class);
+ void generate_delphi_property_reader_impl(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception_class);
+ void generate_delphi_property_writer_impl(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception_class,
+ bool is_union,
+ bool is_xception_factory,
+ std::string xception_factory_name);
+ void generate_delphi_clear_union_value(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception_class,
+ bool is_union,
+ bool is_xception_factory,
+ std::string xception_factory_name);
+ void generate_delphi_isset_reader_impl(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception);
+ void generate_delphi_struct_writer_impl(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception);
+ void generate_delphi_struct_result_writer_impl(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception);
+
+ void generate_delphi_struct_tostring_impl(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_x_factory);
+
+ void add_delphi_uses_list(string unitname);
+
+ void generate_delphi_struct_reader_impl(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception);
+ void generate_delphi_create_exception_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception);
+
+ bool const_needs_var(t_type* type);
+ void print_const_prop(std::ostream& out, string name, t_type* type, t_const_value* value);
+ void print_private_field(std::ostream& out, string name, t_type* type, t_const_value* value);
+ void print_const_value(std::ostream& vars,
+ std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+ void initialize_field(std::ostream& vars,
+ std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+ void finalize_field(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ std::string cls_nm = "");
+ std::string render_const_value(std::ostream& local_vars,
+ std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+ void print_const_def_value(std::ostream& vars,
+ std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ std::string cls_nm = "");
+ std::string make_constants_classname();
+
+ void generate_delphi_struct(t_struct* tstruct, bool is_exception);
+ void generate_delphi_struct_impl(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result = false,
+ bool is_x_factory = false);
+ void print_delphi_struct_type_factory_func(ostream& out, t_struct* tstruct);
+ void generate_delphi_struct_type_factory(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result = false,
+ bool is_x_factory = false);
+ void generate_delphi_struct_type_factory_registration(ostream& out,
+ std::string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result = false,
+ bool is_x_factory = false);
+ void generate_delphi_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool in_class = false,
+ bool is_result = false,
+ bool is_x_factory = false);
+ void generate_delphi_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_delphi_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_delphi_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_delphi_struct_tostring(std::ostream& out, t_struct* tstruct);
+
+ void generate_function_helpers(t_function* tfunction);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_interface(t_service* tservice, bool for_async);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* function);
+
+ void generate_deserialize_field(std::ostream& out,
+ bool is_xception,
+ t_field* tfield,
+ std::string prefix,
+ std::ostream& local_vars);
+ void generate_deserialize_struct(std::ostream& out,
+ t_struct* tstruct,
+ std::string name,
+ std::string prefix);
+ void generate_deserialize_container(ostream& out,
+ bool is_xception,
+ t_type* ttype,
+ string name,
+ std::ostream& local_vars);
+
+ void generate_deserialize_set_element(std::ostream& out,
+ bool is_xception,
+ t_set* tset,
+ std::string prefix,
+ std::ostream& local_vars);
+ void generate_deserialize_map_element(std::ostream& out,
+ bool is_xception,
+ t_map* tmap,
+ std::string prefix,
+ std::ostream& local_vars);
+ void generate_deserialize_list_element(std::ostream& out,
+ bool is_xception,
+ t_list* list,
+ std::string prefix,
+ std::ostream& local_vars);
+
+ void generate_serialize_field(std::ostream& out,
+ bool is_xception,
+ t_field* tfield,
+ std::string prefix,
+ std::ostream& local_vars);
+ void generate_serialize_struct(std::ostream& out,
+ t_struct* tstruct,
+ std::string prefix,
+ std::ostream& local_vars);
+ void generate_serialize_container(std::ostream& out,
+ bool is_xception,
+ t_type* ttype,
+ std::string prefix,
+ std::ostream& local_vars);
+ void generate_serialize_map_element(std::ostream& out,
+ bool is_xception,
+ t_map* tmap,
+ std::string iter,
+ std::string map,
+ std::ostream& local_vars);
+ void generate_serialize_set_element(std::ostream& out,
+ bool is_xception,
+ t_set* tmap,
+ std::string iter,
+ std::ostream& local_vars);
+ void generate_serialize_list_element(std::ostream& out,
+ bool is_xception,
+ t_list* tlist,
+ std::string iter,
+ std::ostream& local_vars);
+
+ void delphi_type_usings(std::ostream& out);
+ std::string delphi_thrift_usings();
+
+ std::string type_name(t_type* ttype,
+ bool b_cls = false,
+ bool b_no_postfix = false,
+ bool b_exception_factory = false,
+ bool b_full_exception_factory = false);
+ std::string normalize_clsnm(std::string name,
+ std::string prefix,
+ bool b_no_check_keyword = false);
+ std::string make_valid_delphi_identifier(std::string const& fromName);
+ std::string input_arg_prefix(t_type* ttype);
+
+ std::string base_type_name(t_base_type* tbase);
+ std::string declare_field(t_field* tfield,
+ bool init = false,
+ std::string prefix = "",
+ bool is_xception_class = false);
+ std::string function_signature(t_function* tfunction,
+ bool for_async,
+ std::string full_cls = "",
+ bool is_xception = false);
+ std::string argument_list(t_struct* tstruct);
+ std::string constructor_argument_list(t_struct* tstruct, std::string current_indent);
+ std::string type_to_enum(t_type* ttype);
+ std::string prop_name(t_field* tfield, bool is_xception = false);
+ std::string prop_name(std::string name, bool is_xception = false);
+ std::string constructor_param_name(string name);
+
+ void write_enum(std::string line);
+ void write_forward_decr(std::string line);
+ void write_const(std::string line);
+ void write_struct(std::string line);
+ void write_service(std::string line);
+
+ std::string autogen_comment() override {
+ return std::string("(**\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
+ + " *)\n";
+ }
+
+ string replace_all(string contents, string search, string replace);
+ string xml_encode(string contents);
+ string xmldoc_encode(string contents);
+ string xmlattrib_encode(string contents);
+ void generate_delphi_doc(std::ostream& out, t_field* field);
+ void generate_delphi_doc(std::ostream& out, t_doc* tdoc);
+ void generate_delphi_doc(std::ostream& out, t_function* tdoc);
+ void generate_delphi_docstring_comment(std::ostream& out, string contents);
+
+ bool type_can_be_null(t_type* ttype) {
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception();
+ }
+
+private:
+ std::string namespace_name_;
+ std::ostringstream s_forward_decr;
+ std::ostringstream s_enum;
+ std::ostringstream s_const;
+ std::ostringstream s_struct;
+ std::ostringstream s_service;
+ std::ostringstream s_const_impl;
+ std::ostringstream s_struct_impl;
+ std::ostringstream s_service_impl;
+ std::ostringstream s_type_factory_registration;
+ std::ostringstream s_type_factory_funcs;
+ bool has_forward;
+ bool has_enum;
+ bool has_const;
+ std::string namespace_dir_;
+ std::map<std::string, int> delphi_keywords;
+ std::map<std::string, int> delphi_reserved_method;
+ std::map<std::string, int> delphi_reserved_method_exception;
+ std::map<std::string, int> types_known;
+ std::list<t_typedef*> typedefs_pending;
+ std::vector<std::string> uses_list;
+ void create_keywords();
+ bool find_keyword(std::map<std::string, int>& keyword_map, std::string name);
+ std::string normalize_name(std::string name,
+ bool b_method = false,
+ bool b_exception_method = false);
+ std::string empty_value(t_type* type);
+ bool is_fully_defined_type(t_type* ttype);
+ void add_defined_type(t_type* ttype);
+ void init_known_types_list();
+ bool is_void(t_type* type);
+ int indent_impl_;
+ bool ansistr_binary_;
+ bool register_types_;
+ bool constprefix_;
+ bool events_;
+ bool xmldoc_;
+ bool async_;
+ void indent_up_impl() { ++indent_impl_; };
+ void indent_down_impl() { --indent_impl_; };
+ std::string indent_impl() {
+ std::string ind = "";
+ int i;
+ for (i = 0; i < indent_impl_; ++i) {
+ ind += " ";
+ }
+ return ind;
+ };
+ std::ostream& indent_impl(std::ostream& os) { return os << indent_impl(); };
+};
+
+string t_delphi_generator::replace_all(string contents, string search, string repl) {
+ string str(contents);
+
+ size_t slen = search.length();
+ size_t rlen = repl.length();
+ size_t incr = (rlen > 0) ? rlen : 1;
+
+ if (slen > 0) {
+ size_t found = str.find(search);
+ while ((found != string::npos) && (found < str.length())) {
+ str.replace(found, slen, repl);
+ found = str.find(search, found + incr);
+ }
+ }
+
+ return str;
+}
+
+// XML encoding
+string t_delphi_generator::xml_encode(string contents) {
+ string str(contents);
+
+ // escape the escape
+ str = replace_all(str, "&", "&amp;");
+
+ // other standard XML entities
+ str = replace_all(str, "<", "&lt;");
+ str = replace_all(str, ">", "&gt;");
+
+ return str;
+}
+
+// XML attribute encoding
+string t_delphi_generator::xmlattrib_encode(string contents) {
+ string str(xml_encode(contents));
+
+ // our attribs are enclosed in "
+ str = replace_all(str, "\"", "\\\"");
+
+ return str;
+}
+
+// XML encoding for doc comments
+string t_delphi_generator::xmldoc_encode(string contents) {
+ string str(xml_encode(contents));
+
+ // XMLDoc specific: convert linebreaks into <para>graphs</para>
+ str = replace_all(str, "\r\n", "\r");
+ str = replace_all(str, "\n", "\r");
+ str = replace_all(str, "\r", "</para>\n<para>");
+
+ return str;
+}
+
+void t_delphi_generator::generate_delphi_docstring_comment(ostream& out, string contents) {
+ if (xmldoc_) {
+ generate_docstring_comment(out,
+ "{$REGION 'XMLDoc'}/// <summary>\n",
+ "/// ",
+ "<para>" + contents + "</para>",
+ "/// </summary>\n{$ENDREGION}\n");
+ }
+}
+
+void t_delphi_generator::generate_delphi_doc(ostream& out, t_field* field) {
+ if (xmldoc_) {
+ if (field->get_type()->is_enum()) {
+ string combined_message = xmldoc_encode(field->get_doc()) + "\n<seealso cref=\""
+ + xmldoc_encode(type_name(field->get_type())) + "\"/>";
+ generate_delphi_docstring_comment(out, combined_message);
+ } else {
+ generate_delphi_doc(out, (t_doc*)field);
+ }
+ }
+}
+
+void t_delphi_generator::generate_delphi_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc() && xmldoc_) {
+ generate_delphi_docstring_comment(out, xmldoc_encode(tdoc->get_doc()));
+ }
+}
+
+void t_delphi_generator::generate_delphi_doc(ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc() && xmldoc_) {
+ stringstream ps;
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ps << "\n<param name=\"" << xmlattrib_encode(p->get_name()) << "\">";
+ if (p->has_doc()) {
+ std::string str = p->get_doc();
+ str.erase(std::remove(str.begin(), str.end(), '\n'),
+ str.end()); // remove the newlines that appear from the parser
+ ps << xmldoc_encode(str);
+ }
+ ps << "</param>";
+ }
+ generate_docstring_comment(out,
+ "{$REGION 'XMLDoc'}",
+ "/// ",
+ "<summary><para>" + xmldoc_encode(tfunction->get_doc())
+ + "</para></summary>" + ps.str(),
+ "{$ENDREGION}\n");
+ }
+}
+
+bool t_delphi_generator::find_keyword(std::map<std::string, int>& keyword_map, std::string name) {
+ std::string::size_type len = name.length();
+
+ if (len <= 0) {
+ return false;
+ }
+
+ std::string::size_type nlast = name.find_last_of('_');
+
+ if (nlast >= 1) {
+ if (nlast == (len - 1)) {
+ string new_name(name, 0, nlast);
+ return find_keyword(keyword_map, new_name);
+ }
+ }
+ return (keyword_map[name] == 1);
+}
+
+std::string t_delphi_generator::normalize_name(std::string name,
+ bool b_method,
+ bool b_exception_method) {
+ string tmp(name);
+ std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int (*)(int)>(std::tolower));
+
+ bool b_found = false;
+
+ if (find_keyword(delphi_keywords, tmp)) {
+ b_found = true;
+ } else if (b_method && find_keyword(delphi_reserved_method, tmp)) {
+ b_found = true;
+ } else if (b_exception_method && find_keyword(delphi_reserved_method_exception, tmp)) {
+ b_found = true;
+ }
+
+ if (b_found) {
+ return name + "_";
+ } else {
+ return name;
+ }
+}
+
+void t_delphi_generator::create_keywords() {
+ delphi_keywords["and"] = 1;
+ delphi_keywords["end"] = 1;
+ delphi_keywords["interface"] = 1;
+ delphi_keywords["raise"] = 1;
+ delphi_keywords["uses"] = 1;
+ delphi_keywords["array"] = 1;
+ delphi_keywords["except"] = 1;
+ delphi_keywords["is"] = 1;
+ delphi_keywords["record"] = 1;
+ delphi_keywords["var"] = 1;
+ delphi_keywords["as"] = 1;
+ delphi_keywords["exports"] = 1;
+ delphi_keywords["label"] = 1;
+ delphi_keywords["repeat"] = 1;
+ delphi_keywords["while"] = 1;
+ delphi_keywords["asm"] = 1;
+ delphi_keywords["file"] = 1;
+ delphi_keywords["library"] = 1;
+ delphi_keywords["resourcestring"] = 1;
+ delphi_keywords["with"] = 1;
+ delphi_keywords["begin"] = 1;
+ delphi_keywords["finalization"] = 1;
+ delphi_keywords["mod"] = 1;
+ delphi_keywords["set"] = 1;
+ delphi_keywords["xor"] = 1;
+ delphi_keywords["case"] = 1;
+ delphi_keywords["finally"] = 1;
+ delphi_keywords["nil"] = 1;
+ delphi_keywords["shl"] = 1;
+ delphi_keywords["class"] = 1;
+ delphi_keywords["for"] = 1;
+ delphi_keywords["not"] = 1;
+ delphi_keywords["shr"] = 1;
+ delphi_keywords["const"] = 1;
+ delphi_keywords["function"] = 1;
+ delphi_keywords["object"] = 1;
+ delphi_keywords["string"] = 1;
+ delphi_keywords["constructor"] = 1;
+ delphi_keywords["goto"] = 1;
+ delphi_keywords["of"] = 1;
+ delphi_keywords["then"] = 1;
+ delphi_keywords["destructor"] = 1;
+ delphi_keywords["if"] = 1;
+ delphi_keywords["or"] = 1;
+ delphi_keywords["threadvar"] = 1;
+ delphi_keywords["dispinterface"] = 1;
+ delphi_keywords["implementation"] = 1;
+ delphi_keywords["out"] = 1;
+ delphi_keywords["to"] = 1;
+ delphi_keywords["div"] = 1;
+ delphi_keywords["in"] = 1;
+ delphi_keywords["packed"] = 1;
+ delphi_keywords["try"] = 1;
+ delphi_keywords["do"] = 1;
+ delphi_keywords["inherited"] = 1;
+ delphi_keywords["procedure"] = 1;
+ delphi_keywords["type"] = 1;
+ delphi_keywords["downto"] = 1;
+ delphi_keywords["initialization"] = 1;
+ delphi_keywords["program"] = 1;
+ delphi_keywords["unit"] = 1;
+ delphi_keywords["else"] = 1;
+ delphi_keywords["inline"] = 1;
+ delphi_keywords["property"] = 1;
+ delphi_keywords["until"] = 1;
+ delphi_keywords["private"] = 1;
+ delphi_keywords["protected"] = 1;
+ delphi_keywords["public"] = 1;
+ delphi_keywords["published"] = 1;
+ delphi_keywords["automated"] = 1;
+ delphi_keywords["at"] = 1;
+ delphi_keywords["on"] = 1;
+
+ // reserved/predefined variables and types (lowercase!)
+ delphi_keywords["result"] = 1;
+ delphi_keywords["system"] = 1;
+ delphi_keywords["sysutils"] = 1;
+ delphi_keywords["thrift"] = 1;
+ delphi_keywords["tbytes"] = 1;
+ delphi_keywords["tobject"] = 1;
+ delphi_keywords["tclass"] = 1;
+ delphi_keywords["tinterfacedobject"] = 1;
+ delphi_keywords["ansistring"] = 1;
+ delphi_keywords["string"] = 1;
+ delphi_keywords["boolean"] = 1;
+ delphi_keywords["shortint"] = 1;
+ delphi_keywords["smallint"] = 1;
+ delphi_keywords["integer"] = 1;
+ delphi_keywords["int64"] = 1;
+ delphi_keywords["double"] = 1;
+
+ delphi_reserved_method["create"] = 1;
+ delphi_reserved_method["free"] = 1;
+ delphi_reserved_method["initinstance"] = 1;
+ delphi_reserved_method["cleanupinstance"] = 1;
+ delphi_reserved_method["classtype"] = 1;
+ delphi_reserved_method["classname"] = 1;
+ delphi_reserved_method["classnameis"] = 1;
+ delphi_reserved_method["classparent"] = 1;
+ delphi_reserved_method["classinfo"] = 1;
+ delphi_reserved_method["instancesize"] = 1;
+ delphi_reserved_method["inheritsfrom"] = 1;
+ delphi_reserved_method["methodaddress"] = 1;
+ delphi_reserved_method["methodname"] = 1;
+ delphi_reserved_method["fieldaddress"] = 1;
+ delphi_reserved_method["getinterface"] = 1;
+ delphi_reserved_method["getinterfaceentry"] = 1;
+ delphi_reserved_method["getinterfacetable"] = 1;
+ delphi_reserved_method["unitname"] = 1;
+ delphi_reserved_method["equals"] = 1;
+ delphi_reserved_method["gethashcode"] = 1;
+ delphi_reserved_method["tostring"] = 1;
+ delphi_reserved_method["safecallexception"] = 1;
+ delphi_reserved_method["afterconstruction"] = 1;
+ delphi_reserved_method["beforedestruction"] = 1;
+ delphi_reserved_method["dispatch"] = 1;
+ delphi_reserved_method["defaulthandler"] = 1;
+ delphi_reserved_method["newinstance"] = 1;
+ delphi_reserved_method["freeinstance"] = 1;
+ delphi_reserved_method["destroy"] = 1;
+ delphi_reserved_method["read"] = 1;
+ delphi_reserved_method["write"] = 1;
+
+ delphi_reserved_method_exception["setinnerexception"] = 1;
+ delphi_reserved_method_exception["setstackinfo"] = 1;
+ delphi_reserved_method_exception["getstacktrace"] = 1;
+ delphi_reserved_method_exception["raisingexception"] = 1;
+ delphi_reserved_method_exception["createfmt"] = 1;
+ delphi_reserved_method_exception["createres"] = 1;
+ delphi_reserved_method_exception["createresfmt"] = 1;
+ delphi_reserved_method_exception["createhelp"] = 1;
+ delphi_reserved_method_exception["createfmthelp"] = 1;
+ delphi_reserved_method_exception["createreshelp"] = 1;
+ delphi_reserved_method_exception["createresfmthelp"] = 1;
+ delphi_reserved_method_exception["getbaseexception"] = 1;
+ delphi_reserved_method_exception["baseexception"] = 1;
+ delphi_reserved_method_exception["helpcontext"] = 1;
+ delphi_reserved_method_exception["innerexception"] = 1;
+ delphi_reserved_method_exception["message"] = 1;
+ delphi_reserved_method_exception["stacktrace"] = 1;
+ delphi_reserved_method_exception["stackinfo"] = 1;
+ delphi_reserved_method_exception["getexceptionstackinfoproc"] = 1;
+ delphi_reserved_method_exception["getstackinfostringproc"] = 1;
+ delphi_reserved_method_exception["cleanupstackinfoproc"] = 1;
+ delphi_reserved_method_exception["raiseouterexception"] = 1;
+ delphi_reserved_method_exception["throwouterexception"] = 1;
+}
+
+void t_delphi_generator::add_delphi_uses_list(string unitname) {
+ vector<std::string>::const_iterator s_iter;
+ bool found = false;
+ for (s_iter = uses_list.begin(); s_iter != uses_list.end(); ++s_iter) {
+ if ((*s_iter) == unitname) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ uses_list.push_back(unitname);
+ }
+}
+
+void t_delphi_generator::init_generator() {
+ indent_impl_ = 0;
+ namespace_name_ = program_->get_namespace("delphi");
+ has_forward = false;
+ has_enum = false;
+ has_const = false;
+ create_keywords();
+
+ add_delphi_uses_list("Classes");
+ add_delphi_uses_list("SysUtils");
+ add_delphi_uses_list("Generics.Collections");
+ if(async_) {
+ add_delphi_uses_list("System.Threading");
+ }
+
+ add_delphi_uses_list("Thrift");
+ add_delphi_uses_list("Thrift.Utils");
+ add_delphi_uses_list("Thrift.Collections");
+ add_delphi_uses_list("Thrift.Protocol");
+ add_delphi_uses_list("Thrift.Transport");
+ if (register_types_) {
+ add_delphi_uses_list("Thrift.TypeRegistry");
+ }
+
+ init_known_types_list();
+
+ string unitname, nsname;
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ unitname = include->get_name();
+ nsname = include->get_namespace("delphi");
+ if ("" != nsname) {
+ unitname = normalize_name(nsname);
+ }
+ add_delphi_uses_list(unitname);
+ }
+
+ MKDIR(get_out_dir().c_str());
+}
+
+void t_delphi_generator::close_generator() {
+ std::string unitname = program_name_;
+ if ("" != namespace_name_) {
+ unitname = namespace_name_;
+ }
+
+ for (int i = 0; i < (int)unitname.size(); i++) {
+ if (unitname[i] == ' ') {
+ unitname.replace(i, 1, "_");
+ }
+ }
+
+ unitname = normalize_name(unitname);
+
+ std::string f_name = get_out_dir() + "/" + unitname + ".pas";
+ ofstream_with_content_based_conditional_update f_all;
+
+ f_all.open(f_name);
+
+ f_all << autogen_comment() << endl;
+ generate_delphi_doc(f_all, program_);
+ f_all << "unit " << unitname << ";" << endl << endl;
+ f_all << "interface" << endl << endl;
+ f_all << "uses" << endl;
+
+ indent_up();
+
+ vector<std::string>::const_iterator s_iter;
+ for (s_iter = uses_list.begin(); s_iter != uses_list.end(); ++s_iter) {
+ if (s_iter != uses_list.begin()) {
+ f_all << ",";
+ f_all << endl;
+ }
+ indent(f_all) << *s_iter;
+ }
+
+ f_all << ";" << endl << endl;
+
+ indent_down();
+
+ string tmp_unit(unitname);
+ for (int i = 0; i < (int)tmp_unit.size(); i++) {
+ if (tmp_unit[i] == '.') {
+ tmp_unit.replace(i, 1, "_");
+ }
+ }
+
+ f_all << "const" << endl;
+ indent_up();
+ indent(f_all) << "c" << tmp_unit
+ << "_Option_AnsiStr_Binary = " << (ansistr_binary_ ? "True" : "False") << ";"
+ << endl;
+ indent(f_all) << "c" << tmp_unit
+ << "_Option_Register_Types = " << (register_types_ ? "True" : "False") << ";"
+ << endl;
+ indent(f_all) << "c" << tmp_unit
+ << "_Option_ConstPrefix = " << (constprefix_ ? "True" : "False") << ";" << endl;
+ indent(f_all) << "c" << tmp_unit << "_Option_Events = " << (events_ ? "True" : "False")
+ << ";" << endl;
+ indent(f_all) << "c" << tmp_unit << "_Option_XmlDoc = " << (xmldoc_ ? "True" : "False")
+ << ";" << endl;
+ indent_down();
+
+ f_all << endl;
+ f_all << "type" << endl;
+ if (has_forward) {
+ f_all << s_forward_decr.str() << endl;
+ }
+ if (has_enum) {
+ indent(f_all) << endl;
+ indent(f_all) << "{$SCOPEDENUMS ON}" << endl << endl;
+ f_all << s_enum.str();
+ indent(f_all) << "{$SCOPEDENUMS OFF}" << endl << endl;
+ }
+ f_all << s_struct.str();
+ f_all << s_service.str();
+ f_all << s_const.str();
+ f_all << "implementation" << endl << endl;
+ f_all << s_struct_impl.str();
+ f_all << s_service_impl.str();
+ f_all << s_const_impl.str();
+
+ if (register_types_) {
+ f_all << endl;
+ f_all << "// Type factory methods and registration" << endl;
+ f_all << s_type_factory_funcs.str();
+ f_all << "procedure RegisterTypeFactories;" << endl;
+ f_all << "begin" << endl;
+ f_all << s_type_factory_registration.str();
+ f_all << "end;" << endl;
+ }
+ f_all << endl;
+
+ string constants_class = make_constants_classname();
+
+ f_all << "initialization" << endl;
+ if (has_const) {
+ f_all << "{$IF CompilerVersion < 21.0} // D2010" << endl;
+ f_all << " " << constants_class.c_str() << "_Initialize;" << endl;
+ f_all << "{$IFEND}" << endl;
+ }
+ if (register_types_) {
+ f_all << " RegisterTypeFactories;" << endl;
+ }
+ f_all << endl;
+
+ f_all << "finalization" << endl;
+ if (has_const) {
+ f_all << "{$IF CompilerVersion < 21.0} // D2010" << endl;
+ f_all << " " << constants_class.c_str() << "_Finalize;" << endl;
+ f_all << "{$IFEND}" << endl;
+ }
+ f_all << endl << endl;
+
+ f_all << "end." << endl;
+ f_all.close();
+
+ if (!typedefs_pending.empty()) {
+ pwarning(0, "%d typedefs with unresolved type references left:\n", typedefs_pending.size());
+ for (std::list<t_typedef*>::iterator iter = typedefs_pending.begin();
+ typedefs_pending.end() != iter;
+ ++iter) {
+ pwarning(0, "- %s\n", (*iter)->get_symbolic().c_str());
+ }
+ }
+}
+
+void t_delphi_generator::delphi_type_usings(ostream& out) {
+ indent_up();
+ indent(out) << "Classes, SysUtils, Generics.Collections, Thrift.Collections, Thrift.Protocol,"
+ << endl;
+ indent(out) << "Thrift.Transport;" << endl << endl;
+ indent_down();
+}
+
+void t_delphi_generator::generate_forward_declaration(t_struct* tstruct) {
+ // Forward declare struct def
+ has_forward = true;
+ pverbose("forward declaration of %s\n", type_name(tstruct).c_str());
+
+ string what = tstruct->is_xception() ? "class" : "interface";
+
+ indent_up();
+ indent(s_forward_decr) << type_name(tstruct, tstruct->is_xception(), true) << " = " << what << ";"
+ << endl;
+ indent_down();
+
+ add_defined_type(tstruct);
+}
+
+void t_delphi_generator::generate_typedef(t_typedef* ttypedef) {
+ t_type* type = ttypedef->get_type();
+
+ // write now or save for later?
+ if (!is_fully_defined_type(type)) {
+ pverbose("typedef %s: unresolved dependencies found\n", type_name(ttypedef).c_str());
+ typedefs_pending.push_back(ttypedef);
+ return;
+ }
+
+ indent_up();
+ generate_delphi_doc(s_struct, ttypedef);
+ indent(s_struct) << type_name(ttypedef) << " = ";
+
+ // commented out: the benefit is not big enough to risk breaking existing code
+ // bool container = type->is_list() || type->is_map() || type->is_set();
+ // if( ! container)
+ // s_struct << "type "; //the "type A = type B" syntax leads to E2574 with generics
+
+ s_struct << type_name(ttypedef->get_type()) << ";" << endl << endl;
+ indent_down();
+
+ add_defined_type(ttypedef);
+}
+
+bool t_delphi_generator::is_fully_defined_type(t_type* ttype) {
+ if ((NULL != ttype->get_program()) && (ttype->get_program() != program_)) {
+ t_scope* scope = ttype->get_program()->scope();
+ if (NULL != scope->get_type(ttype->get_name())) {
+ // printf("type %s found in included scope %s\n", ttype->get_name().c_str(),
+ // ttype->get_program()->get_name().c_str());
+ return true;
+ }
+ }
+
+ if (ttype->is_typedef()) {
+ return (1 == types_known[type_name(ttype)]);
+ }
+
+ if (ttype->is_base_type()) {
+ return (1 == types_known[base_type_name((t_base_type*)ttype)]);
+ } else if (ttype->is_enum()) {
+ return true; // enums are written first, before all other types
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ return is_fully_defined_type(tmap->get_key_type())
+ && is_fully_defined_type(tmap->get_val_type());
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ return is_fully_defined_type(tset->get_elem_type());
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ return is_fully_defined_type(tlist->get_elem_type());
+ }
+
+ return (1 == types_known[type_name(ttype)]);
+}
+
+void t_delphi_generator::add_defined_type(t_type* ttype) {
+ // mark as known type
+ types_known[type_name(ttype)] = 1;
+
+ // check all pending typedefs
+ std::list<t_typedef*>::iterator iter;
+ bool more = true;
+ while (more && (!typedefs_pending.empty())) {
+ more = false;
+
+ for (iter = typedefs_pending.begin(); typedefs_pending.end() != iter; ++iter) {
+ t_typedef* ttypedef = (*iter);
+ if (is_fully_defined_type(ttypedef->get_type())) {
+ pverbose("typedef %s: all pending references are now resolved\n",
+ type_name(ttypedef).c_str());
+ typedefs_pending.erase(iter);
+ generate_typedef(ttypedef);
+ more = true;
+ break;
+ }
+ }
+ }
+}
+
+void t_delphi_generator::init_known_types_list() {
+ // known base types
+ types_known[type_name(g_type_string)] = 1;
+ types_known[type_name(g_type_binary)] = 1;
+ types_known[type_name(g_type_bool)] = 1;
+ types_known[type_name(g_type_i8)] = 1;
+ types_known[type_name(g_type_i16)] = 1;
+ types_known[type_name(g_type_i32)] = 1;
+ types_known[type_name(g_type_i64)] = 1;
+ types_known[type_name(g_type_double)] = 1;
+}
+
+void t_delphi_generator::generate_enum(t_enum* tenum) {
+ has_enum = true;
+ indent_up();
+ generate_delphi_doc(s_enum, tenum);
+ indent(s_enum) << type_name(tenum, true, true) << " = "
+ << "(" << endl;
+ indent_up();
+ vector<t_enum_value*> constants = tenum->get_constants();
+ if (constants.empty()) {
+ indent(s_enum) << "dummy = 0 // empty enums are not allowed";
+ } else {
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ if (c_iter != constants.begin()) {
+ s_enum << ",";
+ s_enum << endl;
+ }
+ generate_delphi_doc(s_enum, *c_iter);
+ indent(s_enum) << normalize_name((*c_iter)->get_name()) << " = " << value;
+ }
+ }
+ s_enum << endl;
+ indent_down();
+ indent(s_enum) << ");" << endl << endl;
+ indent_down();
+}
+
+std::string t_delphi_generator::make_valid_delphi_identifier(std::string const& fromName) {
+ std::string str = fromName;
+ if (str.empty()) {
+ return str;
+ }
+
+ // tests rely on this
+ assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+ // if the first letter is a number, we add an additional underscore in front of it
+ char c = str.at(0);
+ if (('0' <= c) && (c <= '9')) {
+ str = "_" + str;
+ }
+
+ // following chars: letter, number or underscore
+ for (size_t i = 0; i < str.size(); ++i) {
+ c = str.at(i);
+ if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
+ && ('_' != c)) {
+ str.replace(i, 1, "_");
+ }
+ }
+
+ return str;
+}
+
+std::string t_delphi_generator::make_constants_classname() {
+ if (constprefix_) {
+ return make_valid_delphi_identifier("T" + program_name_ + "Constants");
+ } else {
+ return "TConstants"; // compatibility
+ }
+}
+
+void t_delphi_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+
+ has_const = true;
+ string constants_class = make_constants_classname();
+
+ indent_up();
+ indent(s_const) << constants_class.c_str() << " = class" << endl;
+ indent(s_const) << "private" << endl;
+ indent_up();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ if (const_needs_var((*c_iter)->get_type())) {
+ print_private_field(s_const,
+ normalize_name((*c_iter)->get_name()),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value());
+ }
+ }
+ indent_down();
+ indent(s_const) << "public" << endl;
+ indent_up();
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ generate_delphi_doc(s_const, *c_iter);
+ print_const_prop(s_const,
+ normalize_name((*c_iter)->get_name()),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value());
+ }
+ indent(s_const) << "{$IF CompilerVersion >= 21.0}" << endl;
+ indent(s_const) << "class constructor Create;" << endl;
+ indent(s_const) << "class destructor Destroy;" << endl;
+ indent(s_const) << "{$IFEND}" << endl;
+ indent_down();
+ indent(s_const) << "end;" << endl << endl;
+ indent_down();
+
+ std::ostringstream vars, code;
+
+ indent_up_impl();
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ initialize_field(vars,
+ code,
+ "F" + prop_name((*c_iter)->get_name()),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value());
+ }
+ indent_down_impl();
+
+ indent_impl(s_const_impl) << "{$IF CompilerVersion >= 21.0}" << endl;
+ indent_impl(s_const_impl) << "class constructor " << constants_class.c_str() << ".Create;"
+ << endl;
+
+ if (!vars.str().empty()) {
+ indent_impl(s_const_impl) << "var" << endl;
+ s_const_impl << vars.str();
+ }
+ indent_impl(s_const_impl) << "begin" << endl;
+ if (!code.str().empty()) {
+ s_const_impl << code.str();
+ }
+ indent_impl(s_const_impl) << "end;" << endl << endl;
+ indent_impl(s_const_impl) << "class destructor " << constants_class.c_str() << ".Destroy;"
+ << endl;
+ indent_impl(s_const_impl) << "begin" << endl;
+ indent_up_impl();
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ if (const_needs_var((*c_iter)->get_type())) {
+ finalize_field(s_const_impl,
+ normalize_name((*c_iter)->get_name()),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value());
+ }
+ }
+ indent_impl(s_const_impl) << "inherited;" << endl;
+ indent_down_impl();
+ indent_impl(s_const_impl) << "end;" << endl;
+ indent_impl(s_const_impl) << "{$ELSE}" << endl;
+
+ vars.str("");
+ code.str("");
+
+ indent_up_impl();
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ if (const_needs_var((*c_iter)->get_type())) {
+ initialize_field(vars,
+ code,
+ constants_class + ".F" + prop_name((*c_iter)->get_name()),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value());
+ }
+ }
+ indent_down_impl();
+
+ indent_impl(s_const_impl) << "procedure " << constants_class.c_str() << "_Initialize;" << endl;
+ if (!vars.str().empty()) {
+ indent_impl(s_const_impl) << "var" << endl;
+ s_const_impl << vars.str();
+ }
+ indent_impl(s_const_impl) << "begin" << endl;
+ if (!code.str().empty()) {
+ s_const_impl << code.str();
+ }
+ indent_impl(s_const_impl) << "end;" << endl << endl;
+
+ indent_impl(s_const_impl) << "procedure " << constants_class.c_str() << "_Finalize;" << endl;
+ indent_impl(s_const_impl) << "begin" << endl;
+ indent_up_impl();
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ finalize_field(s_const_impl,
+ normalize_name((*c_iter)->get_name()),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ constants_class);
+ }
+ indent_down_impl();
+ indent_impl(s_const_impl) << "end;" << endl;
+ indent_impl(s_const_impl) << "{$IFEND}" << endl << endl;
+}
+
+void t_delphi_generator::print_const_def_value(std::ostream& vars,
+ std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ string cls_nm) {
+
+ string cls_prefix;
+
+ if (cls_nm == "") {
+ cls_prefix = "";
+ } else {
+ cls_prefix = cls_nm + ".";
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(vars, out, name, field_type, v_iter->second);
+ indent_impl(out) << cls_prefix << normalize_name(name) << "."
+ << prop_name(v_iter->first->get_string(), type->is_xception())
+ << " := " << val << ";" << endl;
+ }
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(vars, out, name, ktype, v_iter->first);
+ string val = render_const_value(vars, out, name, vtype, v_iter->second);
+ indent_impl(out) << cls_prefix << normalize_name(name) << "[" << key << "]"
+ << " := " << val << ";" << endl;
+ }
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(vars, out, name, etype, *v_iter);
+ indent_impl(out) << cls_prefix << normalize_name(name) << ".Add(" << val << ");" << endl;
+ }
+ }
+}
+
+void t_delphi_generator::print_private_field(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)value;
+ indent(out) << "class var F" << name << ": " << type_name(type) << ";" << endl;
+}
+
+bool t_delphi_generator::const_needs_var(t_type* type) {
+ t_type* truetype = type;
+ while (truetype->is_typedef()) {
+ truetype = ((t_typedef*)truetype)->get_type();
+ }
+ return (!truetype->is_base_type());
+}
+
+void t_delphi_generator::print_const_prop(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)value;
+ if (const_needs_var(type)) {
+ indent(out) << "class property " << name << ": " << type_name(type) << " read F" << name << ";"
+ << endl;
+ } else {
+ std::ostringstream vars; // dummy
+ string v2 = render_const_value(vars, out, name, type, value);
+ indent(out) << "const " << name << " = " << v2 << ";" << endl;
+ }
+}
+
+void t_delphi_generator::print_const_value(std::ostream& vars,
+ std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ t_type* truetype = type;
+ while (truetype->is_typedef()) {
+ truetype = ((t_typedef*)truetype)->get_type();
+ }
+
+ if (truetype->is_base_type()) {
+ // already done
+ // string v2 = render_const_value( vars, out, name, type, value);
+ // indent_impl(out) << name << " := " << v2 << ";" << endl;
+ } else if (truetype->is_enum()) {
+ indent_impl(out) << name << " := " << type_name(type) << "." << value->get_identifier_name()
+ << ";" << endl;
+ } else {
+ string typname;
+ typname = type_name(truetype, true, false, type->is_xception(), type->is_xception());
+ indent_impl(out) << name << " := " << typname << ".Create;" << endl;
+ print_const_def_value(vars, out, name, truetype, value);
+ }
+}
+
+void t_delphi_generator::initialize_field(std::ostream& vars,
+ std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ print_const_value(vars, out, name, type, value);
+}
+
+void t_delphi_generator::finalize_field(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ string cls_nm) {
+ (void)out;
+ (void)name;
+ (void)type;
+ (void)value;
+ (void)cls_nm;
+}
+
+string t_delphi_generator::render_const_value(ostream& vars,
+ ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+
+ t_type* truetype = type;
+ while (truetype->is_typedef()) {
+ truetype = ((t_typedef*)truetype)->get_type();
+ }
+
+ std::ostringstream render;
+
+ if (truetype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)truetype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << "'" << get_escaped_string(value) << "'";
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "True" : "False");
+ break;
+ case t_base_type::TYPE_I8:
+ render << "ShortInt( " << value->get_integer() << ")";
+ break;
+ case t_base_type::TYPE_I16:
+ render << "SmallInt( " << value->get_integer() << ")";
+ break;
+ case t_base_type::TYPE_I32:
+ render << "LongInt( " << value->get_integer() << ")";
+ break;
+ case t_base_type::TYPE_I64:
+ render << "Int64( " << value->get_integer() << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << value->get_integer() << ".0"; // make it a double constant by adding ".0"
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (truetype->is_enum()) {
+ render << type_name(type, false) << "." << value->get_identifier_name();
+ } else {
+ string t = tmp("tmp");
+ vars << " " << t << " : " << type_name(type) << ";" << endl;
+ print_const_value(vars, out, t, type, value);
+ render << t;
+ }
+
+ return render.str();
+}
+
+void t_delphi_generator::generate_struct(t_struct* tstruct) {
+ generate_delphi_struct(tstruct, false);
+}
+
+void t_delphi_generator::generate_xception(t_struct* txception) {
+ generate_delphi_struct(txception, true);
+}
+
+void t_delphi_generator::generate_delphi_struct(t_struct* tstruct, bool is_exception) {
+ indent_up();
+ generate_delphi_struct_definition(s_struct, tstruct, is_exception);
+ indent_down();
+
+ add_defined_type(tstruct);
+
+ generate_delphi_struct_impl(s_struct_impl, "", tstruct, is_exception);
+ if (register_types_) {
+ generate_delphi_struct_type_factory(s_type_factory_funcs, "", tstruct, is_exception);
+ generate_delphi_struct_type_factory_registration(s_type_factory_registration,
+ "",
+ tstruct,
+ is_exception);
+ }
+}
+
+void t_delphi_generator::generate_delphi_struct_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result,
+ bool is_x_factory) {
+
+ if (is_exception && (!is_x_factory)) {
+ generate_delphi_struct_impl(out, cls_prefix, tstruct, is_exception, is_result, true);
+ }
+
+ string cls_nm;
+
+ string exception_factory_name;
+
+ if (is_exception) {
+ exception_factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory";
+ }
+
+ if (is_exception) {
+ cls_nm = type_name(tstruct, true, (!is_x_factory), is_x_factory, true);
+ } else {
+ cls_nm = type_name(tstruct, true, false);
+ }
+
+ std::ostringstream vars, code;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ indent_up_impl();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef()) {
+ t = ((t_typedef*)t)->get_type();
+ }
+ if ((*m_iter)->get_value() != NULL) {
+ initialize_field(vars,
+ code,
+ "F" + prop_name((*m_iter)->get_name(), is_exception),
+ t,
+ (*m_iter)->get_value());
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ indent_impl(code) << "F__isset_" << prop_name((*m_iter), is_exception) << " := True;"
+ << endl;
+ }
+ }
+ }
+ indent_down_impl();
+
+ indent_impl(out) << "constructor " << cls_prefix << cls_nm << "."
+ << "Create;" << endl;
+
+ if (!vars.str().empty()) {
+ out << "var" << endl;
+ out << vars.str();
+ }
+
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ if (is_exception && (!is_x_factory)) {
+ indent_impl(out) << "inherited Create('');" << endl;
+ } else {
+ indent_impl(out) << "inherited;" << endl;
+ }
+
+ if (!code.str().empty()) {
+ out << code.str();
+ }
+
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+
+ if ((members.size() > 0) && is_exception && (!is_x_factory)) {
+ indent_impl(out) << "constructor " << cls_prefix << cls_nm << "."
+ << "Create(" << constructor_argument_list(tstruct, indent_impl()) << ");"
+ << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(out) << "Create;" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string propname = prop_name((*m_iter)->get_name(), is_exception);
+ string param_name = constructor_param_name((*m_iter)->get_name());
+ indent_impl(out) << propname << " := " << param_name << ";" << endl;
+ }
+ indent_impl(out) << "UpdateMessageProperty;" << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+ }
+
+ indent_impl(out) << "destructor " << cls_prefix << cls_nm << "."
+ << "Destroy;" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef()) {
+ t = ((t_typedef*)t)->get_type();
+ }
+ finalize_field(out, prop_name(*m_iter, is_exception), t, (*m_iter)->get_value());
+ }
+
+ indent_impl(out) << "inherited;" << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+
+ if (is_exception && (!is_x_factory)) {
+ indent_impl(out) << "function " << cls_prefix << cls_nm << "." << exception_factory_name
+ << ": I" << exception_factory_name << ";" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(out) << "if F" << exception_factory_name << " = nil" << endl;
+ indent_impl(out) << "then F" << exception_factory_name << " := T" << exception_factory_name << "Impl.Create;" << endl;
+ indent_impl(out) << endl;
+ indent_impl(out) << "result := F" << exception_factory_name << ";" << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+ }
+
+ if (tstruct->is_union()) {
+ indent_impl(out) << "procedure " << cls_prefix << cls_nm << "."
+ << "ClearUnionValues;" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef()) {
+ t = ((t_typedef*)t)->get_type();
+ }
+
+ generate_delphi_clear_union_value(out,
+ cls_prefix,
+ cls_nm,
+ t,
+ *m_iter,
+ "F",
+ is_exception,
+ tstruct->is_union(),
+ is_x_factory,
+ exception_factory_name);
+ }
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+ }
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef()) {
+ t = ((t_typedef*)t)->get_type();
+ }
+ generate_delphi_property_reader_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception);
+ generate_delphi_property_writer_impl(out,
+ cls_prefix,
+ cls_nm,
+ t,
+ *m_iter,
+ "F",
+ is_exception,
+ tstruct->is_union(),
+ is_x_factory,
+ exception_factory_name);
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ generate_delphi_isset_reader_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception);
+ }
+ }
+
+ if ((!is_exception) || is_x_factory) {
+ generate_delphi_struct_reader_impl(out, cls_prefix, tstruct, is_exception);
+ if (is_result) {
+ generate_delphi_struct_result_writer_impl(out, cls_prefix, tstruct, is_exception);
+ } else {
+ generate_delphi_struct_writer_impl(out, cls_prefix, tstruct, is_exception);
+ }
+ }
+ generate_delphi_struct_tostring_impl(out, cls_prefix, tstruct, is_exception, is_x_factory);
+
+ if (is_exception && is_x_factory) {
+ generate_delphi_create_exception_impl(out, cls_prefix, tstruct, is_exception);
+ }
+}
+
+void t_delphi_generator::print_delphi_struct_type_factory_func(ostream& out, t_struct* tstruct) {
+ string struct_intf_name = type_name(tstruct);
+ out << "Create_";
+ out << struct_intf_name;
+ out << "_Impl";
+}
+
+void t_delphi_generator::generate_delphi_struct_type_factory(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result,
+ bool is_x_factory) {
+ (void)cls_prefix;
+ if (is_exception)
+ return;
+ if (is_result)
+ return;
+ if (is_x_factory)
+ return;
+
+ string struct_intf_name = type_name(tstruct);
+ string cls_nm = type_name(tstruct, true, false);
+
+ out << "function ";
+ print_delphi_struct_type_factory_func(out, tstruct);
+ out << ": ";
+ out << struct_intf_name;
+ out << ";" << endl;
+ out << "begin" << endl;
+ indent_up();
+ indent(out) << "Result := " << cls_nm << ".Create;" << endl;
+ indent_down();
+ out << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_delphi_struct_type_factory_registration(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result,
+ bool is_x_factory) {
+ (void)cls_prefix;
+ if (is_exception)
+ return;
+ if (is_result)
+ return;
+ if (is_x_factory)
+ return;
+
+ string struct_intf_name = type_name(tstruct);
+
+ indent(out) << " TypeRegistry.RegisterTypeFactory<" << struct_intf_name << ">(";
+ print_delphi_struct_type_factory_func(out, tstruct);
+ out << ");";
+ out << endl;
+}
+
+void t_delphi_generator::generate_delphi_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool in_class,
+ bool is_result,
+ bool is_x_factory) {
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+ string struct_intf_name;
+ string struct_name;
+ string isset_name;
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ string exception_factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory";
+
+ if (is_exception) {
+ struct_intf_name = type_name(tstruct, false, false, true);
+ } else {
+ struct_intf_name = type_name(tstruct);
+ }
+
+ if (is_exception) {
+ struct_name = type_name(tstruct, true, (!is_x_factory), is_x_factory);
+ } else {
+ struct_name = type_name(tstruct, true);
+ }
+
+ if ((!is_exception) || is_x_factory) {
+
+ generate_delphi_doc(out, tstruct);
+ indent(out) << struct_intf_name << " = interface(IBase)" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_delphi_property_reader_definition(out, *m_iter, is_exception);
+ generate_delphi_property_writer_definition(out, *m_iter, is_exception);
+ }
+
+ if (is_x_factory) {
+ out << endl;
+ indent(out) << "// Create Exception Object" << endl;
+ indent(out) << "function CreateException: " << type_name(tstruct, true, true) << ";" << endl;
+ }
+
+ if (members.size() > 0) {
+ out << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_property(out, *m_iter, true, is_exception);
+ }
+ }
+
+ if (members.size() > 0) {
+ out << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ generate_delphi_isset_reader_definition(out, *m_iter, is_exception);
+ }
+ }
+ }
+
+ if (members.size() > 0) {
+ out << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ isset_name = "__isset_" + prop_name(*m_iter, is_exception);
+ indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << ";"
+ << endl;
+ }
+ }
+ }
+
+ indent_down();
+ indent(out) << "end;" << endl << endl;
+ }
+
+ generate_delphi_doc(out, tstruct);
+ indent(out) << struct_name << " = ";
+ if (is_final) {
+ out << "sealed ";
+ }
+ out << "class(";
+ if (is_exception && (!is_x_factory)) {
+ out << "TException";
+ } else {
+ out << "TInterfacedObject, IBase, ISupportsToString, " << struct_intf_name;
+ }
+ out << ")" << endl;
+
+ if (is_exception && (!is_x_factory)) {
+ indent(out) << "public" << endl;
+ indent_up();
+ indent(out) << "type" << endl;
+ indent_up();
+ generate_delphi_struct_definition(out, tstruct, is_exception, in_class, is_result, true);
+ indent_down();
+ indent_down();
+ }
+
+ indent(out) << "private" << endl;
+ indent_up();
+
+ if (is_exception && (!is_x_factory)) {
+ indent(out) << "F" << exception_factory_name << " :" << struct_intf_name << ";" << endl << endl;
+ }
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << declare_field(*m_iter, false, "F", is_exception) << endl;
+ }
+
+ if (members.size() > 0) {
+ indent(out) << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ isset_name = "F__isset_" + prop_name(*m_iter, is_exception);
+ indent(out) << isset_name << ": System.Boolean;" << endl;
+ }
+ }
+ }
+
+ indent(out) << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_delphi_property_reader_definition(out, *m_iter, is_exception);
+ generate_delphi_property_writer_definition(out, *m_iter, is_exception);
+ }
+
+ if (tstruct->is_union()) {
+ out << endl;
+ indent(out) << "// Clear values(for union's property setter)" << endl;
+ indent(out) << "procedure ClearUnionValues;" << endl;
+ }
+
+ if (members.size() > 0) {
+ out << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ isset_name = "__isset_" + prop_name(*m_iter, is_exception);
+ indent(out) << "function Get" << isset_name << ": System.Boolean;" << endl;
+ }
+ }
+ }
+
+ indent_down();
+
+ indent(out) << "public" << endl;
+ indent_up();
+
+ if ((members.size() > 0) && is_exception && (!is_x_factory)) {
+ indent(out) << "constructor Create; overload;" << endl;
+ indent(out) << "constructor Create(" << constructor_argument_list(tstruct, indent())
+ << "); overload;" << endl;
+ } else {
+ indent(out) << "constructor Create;" << endl;
+ }
+
+ indent(out) << "destructor Destroy; override;" << endl;
+
+ out << endl;
+ indent(out) << "function ToString: string; override;" << endl;
+
+ if (is_exception && (!is_x_factory)) {
+ out << endl;
+ indent(out) << "// Exception Factory" << endl;
+ indent(out) << "function " << exception_factory_name << ": " << struct_intf_name << ";" << endl;
+ }
+
+ if ((!is_exception) || is_x_factory) {
+ out << endl;
+ indent(out) << "// IBase" << endl;
+ indent(out) << "procedure Read( const iprot: IProtocol);" << endl;
+ indent(out) << "procedure Write( const oprot: IProtocol);" << endl;
+ }
+
+ if (is_exception && is_x_factory) {
+ out << endl;
+ indent(out) << "// Create Exception Object" << endl;
+ indent(out) << "function CreateException: " << type_name(tstruct, true, true) << ";" << endl;
+ }
+
+ if (members.size() > 0) {
+ out << endl;
+ indent(out) << "// Properties" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_property(out, *m_iter, true, is_exception);
+ }
+ }
+
+ if (members.size() > 0) {
+ out << endl;
+ indent(out) << "// isset" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
+ isset_name = "__isset_" + prop_name(*m_iter, is_exception);
+ indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << ";"
+ << endl;
+ }
+ }
+ }
+
+ indent_down();
+ indent(out) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_service(t_service* tservice) {
+ indent_up();
+ generate_delphi_doc(s_service, tservice);
+ indent(s_service) << normalize_clsnm(service_name_, "T") << " = class" << endl;
+ indent(s_service) << "public" << endl;
+ indent_up();
+ indent(s_service) << "type" << endl;
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+ indent_down();
+ indent_down();
+ indent(s_service) << "end;" << endl;
+ indent(s_service) << endl;
+ indent_down();
+}
+
+void t_delphi_generator::generate_service_interface(t_service* tservice) {
+ generate_service_interface(tservice,false);
+ if(async_) {
+ generate_service_interface(tservice,true);
+ }
+}
+
+
+void t_delphi_generator::generate_service_interface(t_service* tservice, bool for_async) {
+ string extends = "";
+ string extends_iface = "";
+ string iface_name = for_async ? "IAsync" : "Iface";
+
+ indent_up();
+
+ generate_delphi_doc(s_service, tservice);
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends(), true, true);
+ extends_iface = extends + "." + iface_name;
+ generate_delphi_doc(s_service, tservice);
+ indent(s_service) << iface_name << " = interface(" << extends_iface << ")" << endl;
+ } else {
+ indent(s_service) << iface_name << " = interface" << endl;
+ }
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_delphi_doc(s_service, *f_iter);
+ indent(s_service) << function_signature(*f_iter, for_async) << endl;
+ }
+ indent_down();
+ indent(s_service) << "end;" << endl << endl;
+
+ indent_down();
+}
+
+void t_delphi_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_delphi_struct_definition(s_service, ts, false, true);
+ generate_delphi_struct_impl(s_service_impl,
+ normalize_clsnm(service_name_, "T") + ".",
+ ts,
+ false);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+void t_delphi_generator::generate_service_client(t_service* tservice) {
+ indent_up();
+ string extends = "";
+ string extends_client = "TInterfacedObject";
+ string implements = async_ ? "Iface, IAsync" : "Iface";
+
+ generate_delphi_doc(s_service, tservice);
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends(), true, true);
+ extends_client = extends + ".TClient";
+ }
+ indent(s_service) << "TClient = class( " << extends_client << ", " << implements << ")" << endl;
+
+ indent(s_service) << "public" << endl;
+ indent_up();
+
+ indent(s_service) << "constructor Create( prot: IProtocol); overload;" << endl;
+
+ indent_impl(s_service_impl) << "constructor " << normalize_clsnm(service_name_, "T")
+ << ".TClient.Create( prot: IProtocol);" << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "Create( prot, prot );" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+
+ indent(s_service)
+ << "constructor Create( const iprot: IProtocol; const oprot: IProtocol); overload;" << endl;
+
+ indent_impl(s_service_impl) << "constructor " << normalize_clsnm(service_name_, "T")
+ << ".TClient.Create( const iprot: IProtocol; const oprot: IProtocol);"
+ << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "inherited Create;" << endl;
+ indent_impl(s_service_impl) << "iprot_ := iprot;" << endl;
+ indent_impl(s_service_impl) << "oprot_ := oprot;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+
+ indent_down();
+
+ if (extends.empty()) {
+ indent(s_service) << "protected" << endl;
+ indent_up();
+ indent(s_service) << "iprot_: IProtocol;" << endl;
+ indent(s_service) << "oprot_: IProtocol;" << endl;
+ indent(s_service) << "seqid_: System.Integer;" << endl;
+ indent_down();
+
+ indent(s_service) << "public" << endl;
+ indent_up();
+ indent(s_service) << "property InputProtocol: IProtocol read iprot_;" << endl;
+ indent(s_service) << "property OutputProtocol: IProtocol read oprot_;" << endl;
+ indent_down();
+ }
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+
+ indent(s_service) << "protected" << endl;
+ indent_up();
+
+ indent(s_service) << "// Iface" << endl;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+ generate_delphi_doc(s_service, *f_iter);
+ indent(s_service) << function_signature(*f_iter, false) << endl;
+ }
+
+ if( async_) {
+ indent(s_service) << endl;
+ indent(s_service) << "// IAsync" << endl;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+ generate_delphi_doc(s_service, *f_iter);
+ indent(s_service) << function_signature(*f_iter, true) << endl;
+ }
+ }
+
+ indent_down();
+
+ indent(s_service) << "public" << endl;
+ indent_up();
+
+ string full_cls = normalize_clsnm(service_name_, "T") + ".TClient";
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+
+ vector<t_field*>::const_iterator fld_iter;
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+
+ // one for sync only, two for async+sync
+ int mode = async_ ? 1 : 0;
+ while( mode >= 0) {
+ bool for_async = (mode != 0);
+ mode--;
+
+ indent_impl(s_service_impl) << function_signature(*f_iter, for_async, full_cls) << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+
+ t_type* ttype = (*f_iter)->get_returntype();
+ if( for_async) {
+ if (is_void(ttype)) {
+ // Delphi forces us to specify a type with IFuture<T>, so we use Integer=0 for void methods
+ indent_impl(s_service_impl) << "result := TTask.Future<System.Integer>(function: System.Integer" << endl;
+ } else {
+ string rettype = type_name(ttype, false, true, false, true);
+ indent_impl(s_service_impl) << "result := TTask.Future<" << rettype << ">(function: " << rettype << endl;
+ }
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ }
+
+ indent_impl(s_service_impl) << "send_" << funname << "(";
+
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ s_service_impl << ", ";
+ }
+ s_service_impl << normalize_name((*fld_iter)->get_name());
+ }
+ s_service_impl << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ s_service_impl << indent_impl();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ s_service_impl << "Result := ";
+ }
+ s_service_impl << "recv_" << funname << "();" << endl;
+ }
+
+ if( for_async) {
+ if (is_void(ttype)) {
+ indent_impl(s_service_impl) << "Result := 0;" << endl; // no IFuture<void> in Delphi
+ }
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end);" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+ }
+
+ t_function send_function(g_type_void,
+ string("send_") + (*f_iter)->get_name(),
+ (*f_iter)->get_arglist());
+
+ string argsname = (*f_iter)->get_name() + "_args";
+ string args_clsnm = normalize_clsnm(argsname, "T");
+ string args_intfnm = normalize_clsnm(argsname, "I");
+
+ string argsvar = tmp("_args");
+ string msgvar = tmp("_msg");
+
+ indent(s_service) << function_signature(&send_function, false) << endl;
+ indent_impl(s_service_impl) << function_signature(&send_function, false, full_cls) << endl;
+ indent_impl(s_service_impl) << "var" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << argsvar << " : " << args_intfnm << ";" << endl;
+ indent_impl(s_service_impl) << msgvar << " : Thrift.Protocol.TThriftMessage;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(s_service_impl) << "seqid_ := seqid_ + 1;" << endl;
+ indent_impl(s_service_impl) << "Thrift.Protocol.Init( " << msgvar << ", '" << funname
+ << "', " << ((*f_iter)->is_oneway() ? "TMessageType.Oneway"
+ : "TMessageType.Call")
+ << ", seqid_);" << endl;
+
+ indent_impl(s_service_impl) << "oprot_.WriteMessageBegin( " << msgvar << " );" << endl;
+ indent_impl(s_service_impl) << argsvar << " := " << args_clsnm << "Impl.Create();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ indent_impl(s_service_impl) << argsvar << "." << prop_name(*fld_iter)
+ << " := " << normalize_name((*fld_iter)->get_name()) << ";"
+ << endl;
+ }
+ indent_impl(s_service_impl) << argsvar << ".Write(oprot_);" << endl;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ indent_impl(s_service_impl) << argsvar << "." << prop_name(*fld_iter)
+ << " := " << empty_value((*fld_iter)->get_type()) << ";" << endl;
+ }
+
+ indent_impl(s_service_impl) << "oprot_.WriteMessageEnd();" << endl;
+ indent_impl(s_service_impl) << "oprot_.Transport.Flush();" << endl;
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ string org_resultname = (*f_iter)->get_name() + "_result";
+ string result_clsnm = normalize_clsnm(org_resultname, "T");
+ string result_intfnm = normalize_clsnm(org_resultname, "I");
+
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs,
+ (*f_iter)->get_xceptions());
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+
+ string exceptvar = tmp("_ex");
+ string appexvar = tmp("_ax");
+ string retvar = tmp("_ret");
+
+ indent(s_service) << function_signature(&recv_function, false) << endl;
+ indent_impl(s_service_impl) << function_signature(&recv_function, false, full_cls) << endl;
+ indent_impl(s_service_impl) << "var" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << msgvar << " : Thrift.Protocol.TThriftMessage;" << endl;
+ if (xceptions.size() > 0) {
+ indent_impl(s_service_impl) << exceptvar << " : Exception;" << endl;
+ }
+ indent_impl(s_service_impl) << appexvar << " : TApplicationException;" << endl;
+ indent_impl(s_service_impl) << retvar << " : " << result_intfnm << ";" << endl;
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << msgvar << " := iprot_.ReadMessageBegin();" << endl;
+ indent_impl(s_service_impl) << "if (" << msgvar << ".Type_ = TMessageType.Exception) then"
+ << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << appexvar << " := TApplicationException.Read(iprot_);" << endl;
+ indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl;
+ indent_impl(s_service_impl) << "raise " << appexvar << ";" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+
+ indent_impl(s_service_impl) << retvar << " := " << result_clsnm << "Impl.Create();" << endl;
+ indent_impl(s_service_impl) << retvar << ".Read(iprot_);" << endl;
+ indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl;
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent_impl(s_service_impl) << "if (" << retvar << ".__isset_success) then" << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "Result := " << retvar << ".Success;" << endl;
+ t_type* type = (*f_iter)->get_returntype();
+ if (type->is_struct() || type->is_xception() || type->is_map() || type->is_list()
+ || type->is_set()) {
+ indent_impl(s_service_impl) << retvar << ".Success := nil;" << endl;
+ }
+ indent_impl(s_service_impl) << "Exit;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ }
+
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent_impl(s_service_impl) << "if (" << retvar << ".__isset_" << prop_name(*x_iter)
+ << ") then" << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << exceptvar << " := " << retvar << "." << prop_name(*x_iter)
+ << ".CreateException;" << endl;
+ indent_impl(s_service_impl) << "raise " << exceptvar << ";" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ }
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent_impl(s_service_impl)
+ << "raise TApplicationExceptionMissingResult.Create('"
+ << (*f_iter)->get_name() << " failed: unknown result');" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+ }
+ }
+
+ indent_down();
+ indent(s_service) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_service_server(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+
+ string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl";
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends(), true, true);
+ extends_processor = extends + ".TProcessorImpl";
+ indent(s_service) << "TProcessorImpl = class(" << extends_processor << ", IProcessor)" << endl;
+ } else {
+ indent(s_service) << "TProcessorImpl = class( TInterfacedObject, IProcessor)" << endl;
+ }
+
+ indent(s_service) << "public" << endl;
+ indent_up();
+ indent(s_service) << "constructor Create( iface_: Iface );" << endl;
+ indent(s_service) << "destructor Destroy; override;" << endl;
+ indent_down();
+
+ indent_impl(s_service_impl) << "constructor " << full_cls << ".Create( iface_: Iface );" << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ if (tservice->get_extends() != NULL) {
+ indent_impl(s_service_impl) << "inherited Create( iface_);" << endl;
+ } else {
+ indent_impl(s_service_impl) << "inherited Create;" << endl;
+ }
+ indent_impl(s_service_impl) << "Self.iface_ := iface_;" << endl;
+ if (tservice->get_extends() != NULL) {
+ indent_impl(s_service_impl) << "ASSERT( processMap_ <> nil); // inherited" << endl;
+ } else {
+ indent_impl(s_service_impl)
+ << "processMap_ := TThriftDictionaryImpl<string, TProcessFunction>.Create;" << endl;
+ }
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent_impl(s_service_impl) << "processMap_.AddOrSetValue( '" << (*f_iter)->get_name() << "', "
+ << (*f_iter)->get_name() << "_Process);" << endl;
+ }
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+
+ indent_impl(s_service_impl) << "destructor " << full_cls << ".Destroy;" << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "inherited;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+
+ indent(s_service) << "private" << endl;
+ indent_up();
+ indent(s_service) << "iface_: Iface;" << endl;
+ indent_down();
+
+ if (tservice->get_extends() == NULL) {
+ indent(s_service) << "protected" << endl;
+ indent_up();
+ indent(s_service) << "type" << endl;
+ indent_up();
+ indent(s_service) << "TProcessFunction = reference to procedure( seqid: System.Integer; const iprot: "
+ "IProtocol; const oprot: IProtocol"
+ << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl;
+ indent_down();
+ indent_down();
+ indent(s_service) << "protected" << endl;
+ indent_up();
+ indent(s_service) << "processMap_: IThriftDictionary<string, TProcessFunction>;" << endl;
+ indent_down();
+ }
+
+ indent(s_service) << "public" << endl;
+ indent_up();
+ if (extends.empty()) {
+ indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol; const "
+ "events : IProcessorEvents): System.Boolean;" << endl;
+ } else {
+ indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol; const "
+ "events : IProcessorEvents): System.Boolean; reintroduce;" << endl;
+ }
+
+ indent_impl(s_service_impl) << "function " << full_cls << ".Process( const iprot: IProtocol; "
+ "const oprot: IProtocol; const events "
+ ": IProcessorEvents): System.Boolean;" << endl;
+ ;
+ indent_impl(s_service_impl) << "var" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "msg : Thrift.Protocol.TThriftMessage;" << endl;
+ indent_impl(s_service_impl) << "fn : TProcessFunction;" << endl;
+ indent_impl(s_service_impl) << "x : TApplicationException;" << endl;
+ if (events_) {
+ indent_impl(s_service_impl) << "context : IRequestEvents;" << endl;
+ }
+ indent_down_impl();
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "try" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "msg := iprot.ReadMessageBegin();" << endl;
+ indent_impl(s_service_impl) << "fn := nil;" << endl;
+ indent_impl(s_service_impl) << "if not processMap_.TryGetValue(msg.Name, fn)" << endl;
+ indent_impl(s_service_impl) << "or not Assigned(fn) then" << endl;
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "TProtocolUtil.Skip(iprot, TType.Struct);" << endl;
+ indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl;
+ indent_impl(s_service_impl) << "x := "
+ "TApplicationExceptionUnknownMethod.Create("
+ "'Invalid method name: ''' + msg.Name + '''');" << endl;
+ indent_impl(s_service_impl)
+ << "Thrift.Protocol.Init( msg, msg.Name, TMessageType.Exception, msg.SeqID);"
+ << endl;
+ indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg);" << endl;
+ indent_impl(s_service_impl) << "x.Write(oprot);" << endl;
+ indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl;
+ indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl;
+ indent_impl(s_service_impl) << "Result := True;" << endl;
+ indent_impl(s_service_impl) << "Exit;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ if (events_) {
+ indent_impl(s_service_impl) << "if events <> nil" << endl;
+ indent_impl(s_service_impl) << "then context := events.CreateRequestContext(msg.Name)" << endl;
+ indent_impl(s_service_impl) << "else context := nil;" << endl;
+ indent_impl(s_service_impl) << "try" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "fn(msg.SeqID, iprot, oprot, context);" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "finally" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "if context <> nil then begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "context.CleanupContext;" << endl;
+ indent_impl(s_service_impl) << "context := nil;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ } else {
+ indent_impl(s_service_impl) << "fn(msg.SeqID, iprot, oprot);" << endl;
+ }
+ indent_down_impl();
+ indent_impl(s_service_impl) << "except" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "on TTransportExceptionTimedOut do begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "Result := True;" << endl;
+ indent_impl(s_service_impl) << "Exit;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ indent_impl(s_service_impl) << "else begin" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "Result := False;" << endl;
+ indent_impl(s_service_impl) << "Exit;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ indent_impl(s_service_impl) << "Result := True;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(s_service) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "Success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_delphi_struct_definition(s_service, &result, false, true, true);
+ generate_delphi_struct_impl(s_service_impl,
+ normalize_clsnm(service_name_, "T") + ".",
+ &result,
+ false);
+}
+
+void t_delphi_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ string funcname = tfunction->get_name();
+ string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl";
+
+ string org_argsname = funcname + "_args";
+ string args_clsnm = normalize_clsnm(org_argsname, "T");
+ string args_intfnm = normalize_clsnm(org_argsname, "I");
+
+ string org_resultname = funcname + "_result";
+ string result_clsnm = normalize_clsnm(org_resultname, "T");
+ string result_intfnm = normalize_clsnm(org_resultname, "I");
+
+ indent(s_service) << "procedure " << funcname
+ << "_Process( seqid: System.Integer; const iprot: IProtocol; const oprot: IProtocol"
+ << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl;
+
+ if (tfunction->is_oneway()) {
+ indent_impl(s_service_impl) << "// one way processor" << endl;
+ } else {
+ indent_impl(s_service_impl) << "// both way processor" << endl;
+ }
+
+ indent_impl(s_service_impl)
+ << "procedure " << full_cls << "." << funcname
+ << "_Process( seqid: System.Integer; const iprot: IProtocol; const oprot: IProtocol"
+ << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl;
+ indent_impl(s_service_impl) << "var" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "args: " << args_intfnm << ";" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_impl(s_service_impl) << "msg: Thrift.Protocol.TThriftMessage;" << endl;
+ indent_impl(s_service_impl) << "ret: " << result_intfnm << ";" << endl;
+ indent_impl(s_service_impl) << "appx : TApplicationException;" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "begin" << endl;
+ indent_up_impl();
+
+ if (events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.PreRead;" << endl;
+ }
+ indent_impl(s_service_impl) << "args := " << args_clsnm << "Impl.Create;" << endl;
+ indent_impl(s_service_impl) << "args.Read(iprot);" << endl;
+ indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl;
+ if (events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.PostRead;" << endl;
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ if (!tfunction->is_oneway()) {
+ indent_impl(s_service_impl) << "ret := " << result_clsnm << "Impl.Create;" << endl;
+ }
+
+ indent_impl(s_service_impl) << "try" << endl;
+ indent_up_impl();
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ s_service_impl << indent_impl();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ s_service_impl << "ret.Success := ";
+ }
+ s_service_impl << "iface_." << normalize_name(tfunction->get_name(), true) << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ s_service_impl << ", ";
+ }
+ s_service_impl << "args." << prop_name(*f_iter);
+ }
+ s_service_impl << ");" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent_impl(s_service_impl) << "args." << prop_name(*f_iter)
+ << " := " << empty_value((*f_iter)->get_type()) << ";" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "except" << endl;
+ indent_up_impl();
+
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent_impl(s_service_impl) << "on E: " << type_name((*x_iter)->get_type(), true, true)
+ << " do begin" << endl;
+ indent_up_impl();
+ if (!tfunction->is_oneway()) {
+ string factory_name = normalize_clsnm((*x_iter)->get_type()->get_name(), "", true)
+ + "Factory";
+ indent_impl(s_service_impl) << "ret." << prop_name(*x_iter) << " := E." << factory_name << ";"
+ << endl;
+ }
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ }
+
+ indent_impl(s_service_impl) << "on E: Exception do begin" << endl;
+ indent_up_impl();
+ if(events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.UnhandledError(E);" << endl;
+ }
+ if (!tfunction->is_oneway()) {
+ indent_impl(s_service_impl) << "appx := TApplicationExceptionInternalError.Create(E.Message);"
+ << endl;
+ indent_impl(s_service_impl) << "try" << endl;
+ indent_up_impl();
+ if(events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.PreWrite;" << endl;
+ }
+ indent_impl(s_service_impl) << "Thrift.Protocol.Init( msg, '"
+ << tfunction->get_name() << "', TMessageType.Exception, seqid);"
+ << endl;
+ indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg);" << endl;
+ indent_impl(s_service_impl) << "appx.Write(oprot);" << endl;
+ indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl;
+ indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl;
+ if(events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.PostWrite;" << endl;
+ }
+ indent_impl(s_service_impl) << "Exit;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "finally" << endl;
+ indent_up_impl();
+ indent_impl(s_service_impl) << "appx.Free;" << endl;
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+ }
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl;
+
+ if (!tfunction->is_oneway()) {
+ if (events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.PreWrite;" << endl;
+ }
+ indent_impl(s_service_impl) << "Thrift.Protocol.Init( msg, '"
+ << tfunction->get_name() << "', TMessageType.Reply, seqid); "
+ << endl;
+ indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg); " << endl;
+ indent_impl(s_service_impl) << "ret.Write(oprot);" << endl;
+ indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl;
+ indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl;
+ if (events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.PostWrite;" << endl;
+ }
+ } else if (events_) {
+ indent_impl(s_service_impl) << "if events <> nil then events.OnewayComplete;" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(s_service_impl) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_deserialize_field(ostream& out,
+ bool is_xception,
+ t_field* tfield,
+ string prefix,
+ ostream& local_vars) {
+ t_type* type = tfield->get_type();
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + prop_name(tfield, is_xception);
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name, "");
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, is_xception, type, name, local_vars);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent_impl(out) << name << " := ";
+
+ if (type->is_enum()) {
+ out << type_name(type, false) << "(";
+ }
+
+ out << "iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ if (ansistr_binary_) {
+ out << "ReadAnsiString();";
+ } else {
+ out << "ReadBinary();";
+ }
+ } else {
+ out << "ReadString();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "ReadBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "ReadByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "ReadI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "ReadI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "ReadI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "ReadDouble();";
+ break;
+ default:
+ throw "compiler error: no Delphi name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "ReadI32()";
+ out << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+void t_delphi_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string name,
+ string prefix) {
+ string typ_name;
+
+ if (tstruct->is_xception()) {
+ typ_name = type_name(tstruct, true, false, true, true);
+ } else {
+ typ_name = type_name(tstruct, true, false);
+ }
+
+ indent_impl(out) << prefix << name << " := " << typ_name << ".Create;" << endl;
+ indent_impl(out) << prefix << name << ".Read(iprot);" << endl;
+}
+
+void t_delphi_generator::generate_deserialize_container(ostream& out,
+ bool is_xception,
+ t_type* ttype,
+ string name,
+ std::ostream& local_vars) {
+
+ string obj;
+ string counter;
+ string local_var;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ if (ttype->is_map()) {
+ local_var = obj + ": TThriftMap;";
+ } else if (ttype->is_set()) {
+ local_var = obj + ": TThriftSet;";
+ } else if (ttype->is_list()) {
+ local_var = obj + ": TThriftList;";
+ }
+ local_vars << " " << local_var << endl;
+ counter = tmp("_i");
+ local_var = counter + ": System.Integer;";
+ local_vars << " " << local_var << endl;
+
+ indent_impl(out) << name << " := " << type_name(ttype, true) << ".Create;" << endl;
+
+ if (ttype->is_map()) {
+ indent_impl(out) << obj << " := iprot.ReadMapBegin();" << endl;
+ } else if (ttype->is_set()) {
+ indent_impl(out) << obj << " := iprot.ReadSetBegin();" << endl;
+ } else if (ttype->is_list()) {
+ indent_impl(out) << obj << " := iprot.ReadListBegin();" << endl;
+ }
+
+ indent_impl(out) << "for " << counter << " := 0 to " << obj << ".Count - 1 do" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, is_xception, (t_map*)ttype, name, local_vars);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, is_xception, (t_set*)ttype, name, local_vars);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, is_xception, (t_list*)ttype, name, local_vars);
+ }
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl;
+
+ if (ttype->is_map()) {
+ indent_impl(out) << "iprot.ReadMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent_impl(out) << "iprot.ReadSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent_impl(out) << "iprot.ReadListEnd();" << endl;
+ }
+}
+
+void t_delphi_generator::generate_deserialize_map_element(ostream& out,
+ bool is_xception,
+ t_map* tmap,
+ string prefix,
+ ostream& local_vars) {
+
+ string key = tmp("_key");
+ string val = tmp("_val");
+ string local_var;
+
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ local_vars << " " << declare_field(&fkey) << endl;
+ local_vars << " " << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, is_xception, &fkey, "", local_vars);
+ generate_deserialize_field(out, is_xception, &fval, "", local_vars);
+
+ indent_impl(out) << prefix << ".AddOrSetValue( " << key << ", " << val << ");" << endl;
+}
+
+void t_delphi_generator::generate_deserialize_set_element(ostream& out,
+ bool is_xception,
+ t_set* tset,
+ string prefix,
+ ostream& local_vars) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+ local_vars << " " << declare_field(&felem) << endl;
+ generate_deserialize_field(out, is_xception, &felem, "", local_vars);
+ indent_impl(out) << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_delphi_generator::generate_deserialize_list_element(ostream& out,
+ bool is_xception,
+ t_list* tlist,
+ string prefix,
+ ostream& local_vars) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+ local_vars << " " << declare_field(&felem) << endl;
+ generate_deserialize_field(out, is_xception, &felem, "", local_vars);
+ indent_impl(out) << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_delphi_generator::generate_serialize_field(ostream& out,
+ bool is_xception,
+ t_field* tfield,
+ string prefix,
+ ostream& local_vars) {
+ (void)local_vars;
+
+ t_type* type = tfield->get_type();
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ string name = prefix + prop_name(tfield, is_xception);
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name, local_vars);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, is_xception, type, name, local_vars);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent_impl(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ if (ansistr_binary_) {
+ out << "WriteAnsiString(";
+ } else {
+ out << "WriteBinary(";
+ }
+ } else {
+ out << "WriteString(";
+ }
+ out << name << ");";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "WriteBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "WriteByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "WriteI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "WriteI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "WriteI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "WriteDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no Delphi name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "WriteI32(System.Integer(" << name << "));";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+void t_delphi_generator::generate_serialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix,
+ ostream& local_vars) {
+ (void)local_vars;
+ (void)tstruct;
+ out << indent_impl() << prefix << ".Write(oprot);" << endl;
+}
+
+void t_delphi_generator::generate_serialize_container(ostream& out,
+ bool is_xception,
+ t_type* ttype,
+ string prefix,
+ ostream& local_vars) {
+ string obj;
+ if (ttype->is_map()) {
+ obj = tmp("map");
+ local_vars << " " << obj << " : TThriftMap;" << endl;
+ indent_impl(out) << "Thrift.Protocol.Init( " << obj << ", "
+ << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix
+ << ".Count);" << endl;
+ indent_impl(out) << "oprot.WriteMapBegin( " << obj << ");" << endl;
+ } else if (ttype->is_set()) {
+ obj = tmp("set_");
+ local_vars << " " << obj << " : TThriftSet;" << endl;
+ indent_impl(out) << "Thrift.Protocol.Init( " << obj << ", "
+ << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix
+ << ".Count);" << endl;
+ indent_impl(out) << "oprot.WriteSetBegin( " << obj << ");" << endl;
+ } else if (ttype->is_list()) {
+ obj = tmp("list_");
+ local_vars << " " << obj << " : TThriftList;" << endl;
+ indent_impl(out) << "Thrift.Protocol.Init( " << obj << ", "
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix
+ << ".Count);" << endl;
+ indent_impl(out) << "oprot.WriteListBegin( " << obj << ");" << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map()) {
+ local_vars << " " << iter << ": " << type_name(((t_map*)ttype)->get_key_type()) << ";" << endl;
+ indent_impl(out) << "for " << iter << " in " << prefix << ".Keys do" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ } else if (ttype->is_set()) {
+ local_vars << " " << iter << ": " << type_name(((t_set*)ttype)->get_elem_type()) << ";"
+ << endl;
+ indent_impl(out) << "for " << iter << " in " << prefix << " do" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ } else if (ttype->is_list()) {
+ local_vars << " " << iter << ": " << type_name(((t_list*)ttype)->get_elem_type()) << ";"
+ << endl;
+ indent_impl(out) << "for " << iter << " in " << prefix << " do" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ }
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, is_xception, (t_map*)ttype, iter, prefix, local_vars);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, is_xception, (t_set*)ttype, iter, local_vars);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, is_xception, (t_list*)ttype, iter, local_vars);
+ }
+
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl;
+
+ if (ttype->is_map()) {
+ indent_impl(out) << "oprot.WriteMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent_impl(out) << "oprot.WriteSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent_impl(out) << "oprot.WriteListEnd();" << endl;
+ }
+}
+
+void t_delphi_generator::generate_serialize_map_element(ostream& out,
+ bool is_xception,
+ t_map* tmap,
+ string iter,
+ string map,
+ ostream& local_vars) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, is_xception, &kfield, "", local_vars);
+ t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+ generate_serialize_field(out, is_xception, &vfield, "", local_vars);
+}
+
+void t_delphi_generator::generate_serialize_set_element(ostream& out,
+ bool is_xception,
+ t_set* tset,
+ string iter,
+ ostream& local_vars) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, is_xception, &efield, "", local_vars);
+}
+
+void t_delphi_generator::generate_serialize_list_element(ostream& out,
+ bool is_xception,
+ t_list* tlist,
+ string iter,
+ ostream& local_vars) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, is_xception, &efield, "", local_vars);
+}
+
+void t_delphi_generator::generate_property(ostream& out,
+ t_field* tfield,
+ bool isPublic,
+ bool is_xception) {
+ generate_delphi_property(out, is_xception, tfield, isPublic, "Get");
+}
+
+void t_delphi_generator::generate_delphi_property(ostream& out,
+ bool struct_is_xception,
+ t_field* tfield,
+ bool isPublic,
+ std::string fieldPrefix) {
+ (void)isPublic;
+
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+ generate_delphi_doc(out, tfield);
+ indent(out) << "property " << prop_name(tfield, struct_is_xception) << ": "
+ << type_name(ftype, false, true, is_xception, true) << " read "
+ << fieldPrefix + prop_name(tfield, struct_is_xception) << " write Set"
+ << prop_name(tfield, struct_is_xception) << ";" << endl;
+}
+
+std::string t_delphi_generator::prop_name(t_field* tfield, bool is_xception) {
+ return prop_name(tfield->get_name(), is_xception);
+}
+
+std::string t_delphi_generator::prop_name(string name, bool is_xception) {
+ string ret = name;
+ ret[0] = toupper(ret[0]);
+ return normalize_name(ret, true, is_xception);
+}
+
+std::string t_delphi_generator::constructor_param_name(string name) {
+ string ret = name;
+ ret[0] = toupper(ret[0]);
+ ret = "A" + ret;
+ return normalize_name(ret, false, false);
+}
+
+string t_delphi_generator::normalize_clsnm(string clsnm, string prefix, bool b_no_check_keyword) {
+ if (clsnm.size() > 0) {
+ clsnm[0] = toupper(clsnm[0]);
+ }
+ if (b_no_check_keyword) {
+ return prefix + clsnm;
+ } else {
+ return normalize_name(prefix + clsnm);
+ }
+}
+
+string t_delphi_generator::type_name(t_type* ttype,
+ bool b_cls,
+ bool b_no_postfix,
+ bool b_exception_factory,
+ bool b_full_exception_factory) {
+
+ if (ttype->is_typedef()) {
+ t_typedef* tdef = (t_typedef*)ttype;
+ if (tdef->is_forward_typedef()) { // forward types according to THRIFT-2421
+ if (tdef->get_type() != NULL) {
+ return type_name(tdef->get_type(),
+ b_cls,
+ b_no_postfix,
+ b_exception_factory,
+ b_full_exception_factory);
+ } else {
+ throw "unresolved forward declaration: " + tdef->get_symbolic();
+ }
+ } else {
+ return normalize_name("T" + tdef->get_symbolic());
+ }
+ }
+
+ string typ_nm;
+
+ string s_factory;
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype);
+ } else if (ttype->is_enum()) {
+ b_cls = true;
+ b_no_postfix = true;
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ if (b_cls) {
+ typ_nm = "TThriftDictionaryImpl";
+ } else {
+ typ_nm = "IThriftDictionary";
+ }
+ return typ_nm + "<" + type_name(tmap->get_key_type()) + ", " + type_name(tmap->get_val_type())
+ + ">";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ if (b_cls) {
+ typ_nm = "THashSetImpl";
+ } else {
+ typ_nm = "IHashSet";
+ }
+ return typ_nm + "<" + type_name(tset->get_elem_type()) + ">";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ if (b_cls) {
+ typ_nm = "TThriftListImpl";
+ } else {
+ typ_nm = "IThriftList";
+ }
+ return typ_nm + "<" + type_name(tlist->get_elem_type()) + ">";
+ }
+
+ string type_prefix;
+
+ if (b_cls) {
+ type_prefix = "T";
+ } else {
+ type_prefix = "I";
+ }
+
+ string nm = normalize_clsnm(ttype->get_name(), type_prefix);
+
+ if (b_exception_factory) {
+ nm = nm + "Factory";
+ }
+
+ if (b_cls) {
+ if (!b_no_postfix) {
+ nm = nm + "Impl";
+ }
+ }
+
+ if (b_exception_factory && b_full_exception_factory) {
+ return type_name(ttype, true, true, false, false) + "." + nm;
+ }
+
+ return nm;
+}
+
+// returns "const " for some argument types
+string t_delphi_generator::input_arg_prefix(t_type* ttype) {
+
+ // base types
+ if (ttype->is_base_type()) {
+ switch (((t_base_type*)ttype)->get_base()) {
+
+ // these should be const'ed for optimal performamce
+ case t_base_type::TYPE_STRING: // refcounted pointer
+ case t_base_type::TYPE_I64: // larger than 32 bit
+ case t_base_type::TYPE_DOUBLE: // larger than 32 bit
+ return "const ";
+
+ // all others don't need to be
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_VOID:
+ return "";
+
+ // we better always report any unknown types
+ default:
+ throw "compiler error: no input_arg_prefix() for base type "
+ + t_base_type::t_base_name(((t_base_type*)ttype)->get_base());
+ }
+
+ // enums
+ } else if (ttype->is_enum()) {
+ return ""; // usually <= 32 bit
+
+ // containers
+ } else if (ttype->is_map()) {
+ return "const "; // refcounted pointer
+
+ } else if (ttype->is_set()) {
+ return "const "; // refcounted pointer
+
+ } else if (ttype->is_list()) {
+ return "const "; // refcounted pointer
+ }
+
+ // any other type, either TSomething or ISomething
+ return "const "; // possibly refcounted pointer
+}
+
+string t_delphi_generator::base_type_name(t_base_type* tbase) {
+ switch (tbase->get_base()) {
+ case t_base_type::TYPE_VOID:
+ // no "void" in Delphi language
+ return "";
+ case t_base_type::TYPE_STRING:
+ if (tbase->is_binary()) {
+ if (ansistr_binary_) {
+ return "System.AnsiString";
+ } else {
+ return "SysUtils.TBytes";
+ }
+ } else {
+ return "System.string";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "System.Boolean";
+ case t_base_type::TYPE_I8:
+ return "System.ShortInt";
+ case t_base_type::TYPE_I16:
+ return "System.SmallInt";
+ case t_base_type::TYPE_I32:
+ return "System.Integer";
+ case t_base_type::TYPE_I64:
+ return "System.Int64";
+ case t_base_type::TYPE_DOUBLE:
+ return "System.Double";
+ default:
+ throw "compiler error: no Delphi name for base type "
+ + t_base_type::t_base_name(tbase->get_base());
+ }
+}
+
+string t_delphi_generator::declare_field(t_field* tfield,
+ bool init,
+ std::string prefix,
+ bool is_xception_class) {
+ (void)init;
+
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+
+ string result = prefix + prop_name(tfield, is_xception_class) + ": "
+ + type_name(ftype, false, true, is_xception, true) + ";";
+ return result;
+}
+
+string t_delphi_generator::function_signature(t_function* tfunction,
+ bool for_async,
+ std::string full_cls,
+ bool is_xception) {
+ t_type* ttype = tfunction->get_returntype();
+ string prefix;
+ if (full_cls == "") {
+ prefix = "";
+ } else {
+ prefix = full_cls + ".";
+ }
+
+ if( for_async) {
+ if (is_void(ttype)) {
+ return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async("
+ + argument_list(tfunction->get_arglist()) + "): IFuture<Integer>;"; // no IFuture<void> in Delphi
+ } else {
+ return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async("
+ + argument_list(tfunction->get_arglist()) + "): IFuture<"
+ + type_name(ttype, false, true, is_xception, true) + ">;";
+ }
+ } else {
+ if (is_void(ttype)) {
+ return "procedure " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "("
+ + argument_list(tfunction->get_arglist()) + ");";
+ } else {
+ return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "("
+ + argument_list(tfunction->get_arglist()) + "): "
+ + type_name(ttype, false, true, is_xception, true) + ";";
+ }
+ }
+}
+
+string t_delphi_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ t_type* tt;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += "; ";
+ }
+
+ tt = (*f_iter)->get_type();
+ result += input_arg_prefix(tt); // const?
+ result += normalize_name((*f_iter)->get_name()) + ": "
+ + type_name(tt, false, true, tt->is_xception(), true);
+ }
+ return result;
+}
+
+string t_delphi_generator::constructor_argument_list(t_struct* tstruct, string current_indent) {
+ ostringstream result;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ t_type* tt;
+ string line = "";
+ string newline_indent = current_indent + " ";
+
+ bool firstline = true;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ line += ";";
+ }
+
+ if (line.size() > 80) {
+ if (firstline) {
+ result << endl << newline_indent;
+ firstline = false;
+ }
+ result << line << endl;
+ line = newline_indent;
+ } else if (line.size() > 0) {
+ line += " ";
+ }
+
+ tt = (*f_iter)->get_type();
+ line += input_arg_prefix(tt); // const?
+ line += constructor_param_name((*f_iter)->get_name()) + ": "
+ + type_name(tt, false, true, tt->is_xception(), true);
+ }
+
+ if (line.size() > 0) {
+ result << line;
+ }
+
+ string result_str;
+
+ if (firstline) {
+ result_str = " " + result.str();
+ } else {
+ result_str = result.str();
+ }
+
+ return result_str;
+}
+
+string t_delphi_generator::type_to_enum(t_type* type) {
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.String_";
+ case t_base_type::TYPE_BOOL:
+ return "TType.Bool_";
+ case t_base_type::TYPE_I8:
+ return "TType.Byte_";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.Double_";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.Struct";
+ } else if (type->is_map()) {
+ return "TType.Map";
+ } else if (type->is_set()) {
+ return "TType.Set_";
+ } else if (type->is_list()) {
+ return "TType.List";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+string t_delphi_generator::empty_value(t_type* type) {
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "0";
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ if (ansistr_binary_) {
+ return "''";
+ } else {
+ return "nil";
+ }
+ } else {
+ return "''";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "False";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "0";
+ case t_base_type::TYPE_DOUBLE:
+ return "0.0";
+ }
+ } else if (type->is_enum()) {
+ return "T" + type->get_name() + "(0)";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "nil";
+ } else if (type->is_map()) {
+ return "nil";
+ } else if (type->is_set()) {
+ return "nil";
+ } else if (type->is_list()) {
+ return "nil";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+void t_delphi_generator::generate_delphi_property_writer_definition(ostream& out,
+ t_field* tfield,
+ bool is_xception_class) {
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+
+ indent(out) << "procedure Set" << prop_name(tfield, is_xception_class)
+ << "( const Value: " << type_name(ftype, false, true, is_xception, true) << ");"
+ << endl;
+}
+
+void t_delphi_generator::generate_delphi_property_reader_definition(ostream& out,
+ t_field* tfield,
+ bool is_xception_class) {
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+
+ indent(out) << "function Get" << prop_name(tfield, is_xception_class) << ": "
+ << type_name(ftype, false, true, is_xception, true) << ";" << endl;
+}
+
+void t_delphi_generator::generate_delphi_isset_reader_definition(ostream& out,
+ t_field* tfield,
+ bool is_xception) {
+ indent(out) << "function Get__isset_" << prop_name(tfield, is_xception) << ": System.Boolean;" << endl;
+}
+
+void t_delphi_generator::generate_delphi_clear_union_value(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception_class,
+ bool is_union,
+ bool is_xception_factory,
+ std::string xception_factory_name) {
+ (void)cls_prefix;
+ (void)name;
+ (void)type;
+ (void)is_union;
+ (void)is_xception_factory;
+ (void)xception_factory_name;
+
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+
+ indent_impl(out) << "if F__isset_" << prop_name(tfield, is_xception_class) << " then begin"
+ << endl;
+ indent_up_impl();
+ indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := False;" << endl;
+ indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := "
+ << "Default( " << type_name(ftype, false, true, is_xception, true) << ");"
+ << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl;
+}
+
+void t_delphi_generator::generate_delphi_property_writer_impl(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception_class,
+ bool is_union,
+ bool is_xception_factory,
+ std::string xception_factory_name) {
+ (void)type;
+
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+
+ indent_impl(out) << "procedure " << cls_prefix << name << "."
+ << "Set" << prop_name(tfield, is_xception_class)
+ << "( const Value: " << type_name(ftype, false, true, is_xception, true) << ");"
+ << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ if (is_union) {
+ indent_impl(out) << "ClearUnionValues;" << endl;
+ }
+ if (tfield->get_req() != t_field::T_REQUIRED) {
+ indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := True;" << endl;
+ }
+ indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := Value;" << endl;
+
+ if (is_xception_class && (!is_xception_factory)) {
+ indent_impl(out) << xception_factory_name << "." << prop_name(tfield, is_xception_class)
+ << " := Value;" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_delphi_property_reader_impl(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception_class) {
+ (void)type;
+
+ t_type* ftype = tfield->get_type();
+ bool is_xception = ftype->is_xception();
+
+ indent_impl(out) << "function " << cls_prefix << name << "."
+ << "Get" << prop_name(tfield, is_xception_class) << ": "
+ << type_name(ftype, false, true, is_xception, true) << ";" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(out) << "Result := " << fieldPrefix << prop_name(tfield, is_xception_class) << ";"
+ << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_delphi_isset_reader_impl(ostream& out,
+ std::string cls_prefix,
+ std::string name,
+ t_type* type,
+ t_field* tfield,
+ std::string fieldPrefix,
+ bool is_xception) {
+ (void)type;
+
+ string isset_name = "__isset_" + prop_name(tfield, is_xception);
+ indent_impl(out) << "function " << cls_prefix << name << "."
+ << "Get" << isset_name << ": System.Boolean;" << endl;
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(out) << "Result := " << fieldPrefix << isset_name << ";" << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_delphi_create_exception_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception) {
+ (void)cls_prefix;
+
+ string exception_cls_nm = type_name(tstruct, true, true);
+ string cls_nm = type_name(tstruct, true, false, is_exception, is_exception);
+
+ indent_impl(out) << "function " << cls_nm << ".CreateException: " << exception_cls_nm << ";"
+ << endl;
+
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(out) << "Result := " << exception_cls_nm << ".Create;" << endl;
+ string factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory";
+ indent_impl(out) << "Result.F" << factory_name << " := Self;" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ string propname;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ propname = prop_name(*f_iter, is_exception);
+ if ((*f_iter)->get_req() != t_field::T_REQUIRED) {
+ indent_impl(out) << "if __isset_" << propname << " then begin" << endl;
+ indent_up_impl();
+ }
+ indent_impl(out) << "Result." << propname << " := " << propname << ";" << endl;
+ if ((*f_iter)->get_req() != t_field::T_REQUIRED) {
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl;
+ }
+ }
+
+ indent_impl(out) << "Result.UpdateMessageProperty;" << endl;
+
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+}
+
+void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception) {
+
+ ostringstream local_vars;
+ ostringstream code_block;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent_impl(code_block) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl;
+ indent_impl(code_block) << "tracker := iprot.NextRecursionLevel;" << endl;
+
+ // local bools for required fields
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ indent_impl(local_vars) << "_req_isset_" << prop_name(*f_iter, is_exception) << " : System.Boolean;"
+ << endl;
+ indent_impl(code_block) << "_req_isset_" << prop_name(*f_iter, is_exception) << " := FALSE;"
+ << endl;
+ }
+ }
+
+ indent_impl(code_block) << "struc := iprot.ReadStructBegin;" << endl;
+
+ indent_impl(code_block) << "try" << endl;
+ indent_up_impl();
+
+ indent_impl(code_block) << "while (true) do" << endl;
+ indent_impl(code_block) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(code_block) << "field_ := iprot.ReadFieldBegin();" << endl;
+
+ indent_impl(code_block) << "if (field_.Type_ = TType.Stop) then" << endl;
+ indent_impl(code_block) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(code_block) << "Break;" << endl;
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl;
+
+ bool first = true;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+
+ if (first) {
+ indent_impl(code_block) << "case field_.ID of" << endl;
+ indent_up_impl();
+ }
+
+ first = false;
+ if (f_iter != fields.begin()) {
+ code_block << ";" << endl;
+ }
+ indent_impl(code_block) << (*f_iter)->get_key() << ": begin" << endl;
+ indent_up_impl();
+ indent_impl(code_block) << "if (field_.Type_ = " << type_to_enum((*f_iter)->get_type())
+ << ") then begin" << endl;
+ indent_up_impl();
+
+ generate_deserialize_field(code_block, is_exception, *f_iter, "Self.", local_vars);
+
+ // required field?
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ indent_impl(code_block) << "_req_isset_" << prop_name(*f_iter, is_exception) << " := TRUE;"
+ << endl;
+ }
+
+ indent_down_impl();
+
+ indent_impl(code_block) << "end else begin" << endl;
+ indent_up_impl();
+ indent_impl(code_block) << "TProtocolUtil.Skip(iprot, field_.Type_);" << endl;
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl;
+ indent_down_impl();
+ indent_impl(code_block) << "end";
+ }
+
+ if (!first) {
+ code_block << endl;
+ indent_impl(code_block) << "else begin" << endl;
+ indent_up_impl();
+ }
+
+ indent_impl(code_block) << "TProtocolUtil.Skip(iprot, field_.Type_);" << endl;
+
+ if (!first) {
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl;
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl;
+ }
+
+ indent_impl(code_block) << "iprot.ReadFieldEnd;" << endl;
+
+ indent_down_impl();
+
+ indent_impl(code_block) << "end;" << endl;
+ indent_down_impl();
+
+ indent_impl(code_block) << "finally" << endl;
+ indent_up_impl();
+ indent_impl(code_block) << "iprot.ReadStructEnd;" << endl;
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl;
+
+ // all required fields have been read?
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ indent_impl(code_block) << "if not _req_isset_" << prop_name(*f_iter, is_exception) << endl;
+ indent_impl(code_block)
+ << "then raise TProtocolExceptionInvalidData.Create("
+ << "'required field " << prop_name(*f_iter, is_exception) << " not set');"
+ << endl;
+ }
+ }
+
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl << endl;
+
+ string cls_nm;
+
+ cls_nm = type_name(tstruct, true, false, is_exception, is_exception);
+
+ indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Read( const iprot: IProtocol);"
+ << endl;
+ indent_impl(out) << "var" << endl;
+ indent_up_impl();
+ indent_impl(out) << "field_ : TThriftField;" << endl;
+ indent_impl(out) << "struc : TThriftStruct;" << endl;
+ indent_down_impl();
+ out << local_vars.str() << endl;
+ out << code_block.str();
+}
+
+void t_delphi_generator::generate_delphi_struct_result_writer_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception) {
+
+ ostringstream local_vars;
+ ostringstream code_block;
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent_impl(code_block) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl;
+ indent_impl(code_block) << "tracker := oprot.NextRecursionLevel;" << endl;
+
+ indent_impl(code_block) << "Thrift.Protocol.Init( struc, '" << name << "');" << endl;
+ indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl;
+
+ if (fields.size() > 0) {
+ indent_impl(code_block) << "Thrift.Protocol.Init( field_);" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent_impl(code_block) << "if (__isset_" << prop_name(*f_iter, is_exception) << ") then"
+ << endl;
+ indent_impl(code_block) << "begin" << endl;
+ indent_up_impl();
+ indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl;
+ indent_impl(code_block) << "field_.Type_ := " << type_to_enum((*f_iter)->get_type()) << ";"
+ << endl;
+ indent_impl(code_block) << "field_.ID := " << (*f_iter)->get_key() << ";" << endl;
+ indent_impl(code_block) << "oprot.WriteFieldBegin(field_);" << endl;
+ generate_serialize_field(code_block, is_exception, *f_iter, "Self.", local_vars);
+ indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl;
+ indent_down_impl();
+ }
+ }
+
+ indent_impl(code_block) << "oprot.WriteFieldStop();" << endl;
+ indent_impl(code_block) << "oprot.WriteStructEnd();" << endl;
+
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl << endl;
+
+ string cls_nm;
+
+ cls_nm = type_name(tstruct, true, false, is_exception, is_exception);
+
+ indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);"
+ << endl;
+ indent_impl(out) << "var" << endl;
+ indent_up_impl();
+ indent_impl(out) << "struc : TThriftStruct;" << endl;
+
+ if (fields.size() > 0) {
+ indent_impl(out) << "field_ : TThriftField;" << endl;
+ }
+
+ out << local_vars.str();
+ indent_down_impl();
+ out << code_block.str();
+}
+
+void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception) {
+
+ ostringstream local_vars;
+ ostringstream code_block;
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent_impl(code_block) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl;
+ indent_impl(code_block) << "tracker := oprot.NextRecursionLevel;" << endl;
+
+ indent_impl(code_block) << "Thrift.Protocol.Init( struc, '" << name << "');" << endl;
+ indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl;
+
+ if (fields.size() > 0) {
+ indent_impl(code_block) << "Thrift.Protocol.Init( field_);" << endl;
+ }
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ string fieldname = prop_name((*f_iter), is_exception);
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ bool is_required = ((*f_iter)->get_req() == t_field::T_REQUIRED);
+ bool has_isset = (!is_required);
+ if (is_required && null_allowed) {
+ null_allowed = false;
+ indent_impl(code_block) << "if (Self." << fieldname << " = nil)" << endl;
+ indent_impl(code_block) << "then raise TProtocolExceptionInvalidData.Create("
+ << "'required field " << fieldname << " not set');"
+ << endl;
+ }
+ if (null_allowed) {
+ indent_impl(code_block) << "if (Self." << fieldname << " <> nil)";
+ if (has_isset) {
+ code_block << " and __isset_" << fieldname;
+ }
+ code_block << " then begin" << endl;
+ indent_up_impl();
+ } else {
+ if (has_isset) {
+ indent_impl(code_block) << "if (__isset_" << fieldname << ") then begin" << endl;
+ indent_up_impl();
+ }
+ }
+ indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl;
+ indent_impl(code_block) << "field_.Type_ := " << type_to_enum((*f_iter)->get_type()) << ";"
+ << endl;
+ indent_impl(code_block) << "field_.ID := " << (*f_iter)->get_key() << ";" << endl;
+ indent_impl(code_block) << "oprot.WriteFieldBegin(field_);" << endl;
+ generate_serialize_field(code_block, is_exception, *f_iter, "Self.", local_vars);
+ indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl;
+ if (null_allowed || has_isset) {
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl;
+ }
+ }
+
+ indent_impl(code_block) << "oprot.WriteFieldStop();" << endl;
+ indent_impl(code_block) << "oprot.WriteStructEnd();" << endl;
+
+ indent_down_impl();
+ indent_impl(code_block) << "end;" << endl << endl;
+
+ string cls_nm;
+
+ cls_nm = type_name(tstruct, true, false, is_exception, is_exception);
+
+ indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);"
+ << endl;
+ indent_impl(out) << "var" << endl;
+ indent_up_impl();
+ indent_impl(out) << "struc : TThriftStruct;" << endl;
+ if (fields.size() > 0) {
+ indent_impl(out) << "field_ : TThriftField;" << endl;
+ }
+ out << local_vars.str();
+ indent_down_impl();
+ out << code_block.str();
+}
+
+void t_delphi_generator::generate_delphi_struct_tostring_impl(ostream& out,
+ string cls_prefix,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_x_factory) {
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ string cls_nm;
+
+ if (is_exception) {
+ cls_nm = type_name(tstruct, true, (!is_x_factory), is_x_factory, true);
+ } else {
+ cls_nm = type_name(tstruct, true, false);
+ }
+
+ string tmp_sb = tmp("_sb");
+ string tmp_first = tmp("_first");
+ bool useFirstFlag = false;
+
+ indent_impl(out) << "function " << cls_prefix << cls_nm << ".ToString: string;" << endl;
+ indent_impl(out) << "var" << endl;
+ indent_up_impl();
+ indent_impl(out) << tmp_sb << " : TThriftStringBuilder;" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED);
+ if (is_optional) {
+ indent_impl(out) << tmp_first << " : System.Boolean;" << endl;
+ useFirstFlag = true;
+ }
+ break;
+ }
+ indent_down_impl();
+ indent_impl(out) << "begin" << endl;
+ indent_up_impl();
+
+ indent_impl(out) << tmp_sb << " := TThriftStringBuilder.Create('(');" << endl;
+ indent_impl(out) << "try" << endl;
+ indent_up_impl();
+
+ if (useFirstFlag) {
+ indent_impl(out) << tmp_first << " := TRUE;" << endl;
+ }
+
+ bool had_required = false; // set to true after first required field has been processed
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED);
+ if (null_allowed) {
+ indent_impl(out) << "if (Self." << prop_name((*f_iter), is_exception) << " <> nil)";
+ if (is_optional) {
+ out << " and __isset_" << prop_name(*f_iter, is_exception);
+ }
+ out << " then begin" << endl;
+ indent_up_impl();
+ } else {
+ if (is_optional) {
+ indent_impl(out) << "if (__isset_" << prop_name(*f_iter, is_exception) << ") then begin"
+ << endl;
+ indent_up_impl();
+ }
+ }
+
+ if (useFirstFlag && (!had_required)) {
+ indent_impl(out) << "if not " << tmp_first << " then " << tmp_sb << ".Append(',');" << endl;
+ if (is_optional) {
+ indent_impl(out) << tmp_first << " := FALSE;" << endl;
+ }
+ indent_impl(out) << tmp_sb << ".Append('" << prop_name((*f_iter), is_exception) << ": ');"
+ << endl;
+ } else {
+ indent_impl(out) << tmp_sb << ".Append(', " << prop_name((*f_iter), is_exception) << ": ');"
+ << endl;
+ }
+
+ t_type* ttype = (*f_iter)->get_type();
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ if (ttype->is_xception() || ttype->is_struct()) {
+ indent_impl(out) << "if (Self." << prop_name((*f_iter), is_exception) << " = nil) then " << tmp_sb
+ << ".Append('<null>') else " << tmp_sb << ".Append( Self."
+ << prop_name((*f_iter), is_exception) << ".ToString());" << endl;
+ } else if (ttype->is_enum()) {
+ indent_impl(out) << tmp_sb << ".Append(EnumUtils<"
+ << type_name(ttype, false, true, false, false)
+ << ">.ToString( System.Ord( Self."
+ << prop_name((*f_iter), is_exception) << ")));" << endl;
+ } else {
+ indent_impl(out) << tmp_sb << ".Append( Self." << prop_name((*f_iter), is_exception) << ");"
+ << endl;
+ }
+
+ if (null_allowed || is_optional) {
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl;
+ }
+
+ if (!is_optional) {
+ had_required = true; // now __first must be false, so we don't need to check it anymore
+ }
+ }
+
+ indent_impl(out) << tmp_sb << ".Append(')');" << endl;
+ indent_impl(out) << "Result := " << tmp_sb << ".ToString;" << endl;
+ if (useFirstFlag) {
+ indent_impl(out) << "if " << tmp_first << " then {prevent warning};" << endl;
+ }
+
+ indent_down_impl();
+ indent_impl(out) << "finally" << endl;
+ indent_up_impl();
+ indent_impl(out) << tmp_sb << ".Free;" << endl;
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl;
+
+ indent_down_impl();
+ indent_impl(out) << "end;" << endl << endl;
+}
+
+bool t_delphi_generator::is_void(t_type* type) {
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ if (tbase == t_base_type::TYPE_VOID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+THRIFT_REGISTER_GENERATOR(
+ delphi,
+ "delphi",
+ " ansistr_binary: Use AnsiString for binary datatype (default is TBytes).\n"
+ " register_types: Enable TypeRegistry, allows for creation of struct, union\n"
+ " and container instances by interface or TypeInfo()\n"
+ " constprefix: Name TConstants classes after IDL to reduce ambiguities\n"
+ " events: Enable and use processing events in the generated code.\n"
+ " xmldoc: Enable XMLDoc comments for Help Insight etc.\n"
+ " async: Generate IAsync interface to use Parallel Programming Library (XE7+ only).\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc
new file mode 100644
index 000000000..910a38a23
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc
@@ -0,0 +1,1282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const std::string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Erlang code generator.
+ *
+ */
+class t_erl_generator : public t_generator {
+public:
+ t_erl_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ legacy_names_ = false;
+ maps_ = false;
+ otp16_ = false;
+ export_lines_first_ = true;
+ export_types_lines_first_ = true;
+
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("legacynames") == 0) {
+ legacy_names_ = true;
+ } else if( iter->first.compare("maps") == 0) {
+ maps_ = true;
+ } else if( iter->first.compare("otp16") == 0) {
+ otp16_ = true;
+ } else {
+ throw "unknown option erl:" + iter->first;
+ }
+ }
+
+ if (maps_ && otp16_) {
+ throw "argument error: Cannot specify both maps and otp16; maps are not available for Erlang/OTP R16 or older";
+ }
+
+ out_dir_base_ = "gen-erl";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+ void generate_member_type(std::ostream& out, t_type* type);
+ void generate_member_value(std::ostream& out, t_type* type, t_const_value* value);
+
+ std::string render_member_type(t_field* field);
+ std::string render_member_value(t_field* field);
+ std::string render_member_requiredness(t_field* field);
+
+ // std::string render_default_value(t_type* type);
+ std::string render_default_value(t_field* field);
+ std::string render_const_value(t_type* type, t_const_value* value);
+ std::string render_type_term(t_type* ttype, bool expand_structs, bool extended_info = false);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_erl_struct(t_struct* tstruct, bool is_exception);
+ void generate_erl_struct_definition(std::ostream& out, t_struct* tstruct);
+ void generate_erl_struct_member(std::ostream& out, t_field* tmember);
+ void generate_erl_struct_info(std::ostream& out, t_struct* tstruct);
+ void generate_erl_extended_struct_info(std::ostream& out, t_struct* tstruct);
+ void generate_erl_function_helpers(t_function* tfunction);
+ void generate_type_metadata(std::string function_name, vector<string> names);
+ void generate_enum_info(t_enum* tenum);
+ void generate_enum_metadata();
+ void generate_const_function(t_const* tconst, ostringstream& exports, ostringstream& functions);
+ void generate_const_functions();
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_metadata(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_function_info(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string erl_autogen_comment();
+ std::string erl_imports();
+ std::string render_includes();
+ std::string type_name(t_type* ttype);
+ std::string render_const_list_values(t_type* type, t_const_value* value);
+
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string type_module(t_type* ttype);
+
+ std::string make_safe_for_module_name(std::string in) {
+ if (legacy_names_) {
+ return decapitalize(in);
+ } else {
+ return underscore(in);
+ }
+ }
+
+ std::string atomify(std::string in) {
+ if (legacy_names_) {
+ return "'" + decapitalize(in) + "'";
+ } else {
+ return "'" + in + "'";
+ }
+ }
+
+ std::string constify(std::string in) {
+ if (legacy_names_) {
+ return capitalize(in);
+ } else {
+ return uppercase(in);
+ }
+ }
+
+ static std::string comment(string in);
+
+private:
+ bool has_default_value(t_field*);
+
+ /* if true retain pre 0.9.2 naming scheme for functions, atoms and consts */
+ bool legacy_names_;
+
+ /* if true use maps instead of dicts in generated code */
+ bool maps_;
+
+ /* if true use non-namespaced dict and set instead of dict:dict and sets:set */
+ bool otp16_;
+
+ /**
+ * add function to export list
+ */
+
+ void export_function(t_function* tfunction, std::string prefix = "");
+ void export_string(std::string name, int num);
+
+ void export_types_string(std::string name, int num);
+
+ /**
+ * write out headers and footers for hrl files
+ */
+
+ void hrl_header(std::ostream& out, std::string name);
+ void hrl_footer(std::ostream& out, std::string name);
+
+ /**
+ * stuff to spit out at the top of generated files
+ */
+
+ bool export_lines_first_;
+ std::ostringstream export_lines_;
+
+ bool export_types_lines_first_;
+ std::ostringstream export_types_lines_;
+
+ /**
+ * File streams
+ */
+
+ std::ostringstream f_info_;
+ std::ostringstream f_info_ext_;
+
+ ofstream_with_content_based_conditional_update f_types_file_;
+ ofstream_with_content_based_conditional_update f_types_hrl_file_;
+
+ ofstream_with_content_based_conditional_update f_consts_file_;
+ ofstream_with_content_based_conditional_update f_consts_hrl_file_;
+
+ std::ostringstream f_service_;
+ ofstream_with_content_based_conditional_update f_service_file_;
+ ofstream_with_content_based_conditional_update f_service_hrl_;
+
+ /**
+ * Metadata containers
+ */
+ std::vector<std::string> v_struct_names_;
+ std::vector<std::string> v_enum_names_;
+ std::vector<std::string> v_exception_names_;
+ std::vector<t_enum*> v_enums_;
+ std::vector<t_const*> v_consts_;
+};
+
+/**
+ * UI for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_erl_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ // setup export lines
+ export_lines_first_ = true;
+ export_types_lines_first_ = true;
+
+ string program_module_name = make_safe_for_module_name(program_name_);
+
+ // types files
+ string f_types_name = get_out_dir() + program_module_name + "_types.erl";
+ string f_types_hrl_name = get_out_dir() + program_module_name + "_types.hrl";
+
+ f_types_file_.open(f_types_name.c_str());
+ f_types_hrl_file_.open(f_types_hrl_name.c_str());
+
+ hrl_header(f_types_hrl_file_, program_module_name + "_types");
+
+ f_types_file_ << erl_autogen_comment() << endl
+ << "-module(" << program_module_name << "_types)." << endl
+ << erl_imports() << endl;
+
+ f_types_file_ << "-include(\"" << program_module_name << "_types.hrl\")." << endl
+ << endl;
+
+ f_types_hrl_file_ << render_includes() << endl;
+
+ // consts files
+ string f_consts_name = get_out_dir() + program_module_name + "_constants.erl";
+ string f_consts_hrl_name = get_out_dir() + program_module_name + "_constants.hrl";
+
+ f_consts_file_.open(f_consts_name.c_str());
+ f_consts_hrl_file_.open(f_consts_hrl_name.c_str());
+
+ f_consts_file_ << erl_autogen_comment() << endl
+ << "-module(" << program_module_name << "_constants)." << endl
+ << erl_imports() << endl
+ << "-include(\"" << program_module_name << "_types.hrl\")." << endl
+ << endl;
+
+ f_consts_hrl_file_ << erl_autogen_comment() << endl << erl_imports() << endl
+ << "-include(\"" << program_module_name << "_types.hrl\")." << endl << endl;
+}
+
+/**
+ * Boilerplate at beginning and end of header files
+ */
+void t_erl_generator::hrl_header(ostream& out, string name) {
+ out << erl_autogen_comment() << endl
+ << "-ifndef(_" << name << "_included)." << endl << "-define(_" << name << "_included, yeah)."
+ << endl;
+}
+
+void t_erl_generator::hrl_footer(ostream& out, string name) {
+ (void)name;
+ out << "-endif." << endl;
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_erl_generator::render_includes() {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ for (auto include : includes) {
+ result += "-include(\"" + make_safe_for_module_name(include->get_name())
+ + "_types.hrl\").\n";
+ }
+ if (includes.size() > 0) {
+ result += "\n";
+ }
+ return result;
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_erl_generator::erl_autogen_comment() {
+ return std::string("%%\n") + "%% Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + "%%\n" + "%% DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
+ + "%%\n";
+}
+
+/**
+ * Comment out text
+ */
+
+string t_erl_generator::comment(string in) {
+ size_t pos = 0;
+ in.insert(pos, "%% ");
+ while ((pos = in.find_first_of('\n', pos)) != string::npos) {
+ in.insert(++pos, "%% ");
+ }
+ return in;
+}
+
+/**
+ * Prints standard thrift imports
+ */
+string t_erl_generator::erl_imports() {
+ return "";
+}
+
+/**
+ * Closes the type files
+ */
+void t_erl_generator::close_generator() {
+
+ export_types_string("struct_info", 1);
+ export_types_string("struct_info_ext", 1);
+ export_types_string("enum_info", 1);
+ export_types_string("enum_names", 0);
+ export_types_string("struct_names", 0);
+ export_types_string("exception_names", 0);
+
+ f_types_file_ << "-export([" << export_types_lines_.str() << "])." << endl << endl;
+
+ f_types_file_ << f_info_.str();
+ f_types_file_ << "struct_info(_) -> erlang:error(function_clause)." << endl << endl;
+
+ f_types_file_ << f_info_ext_.str();
+ f_types_file_ << "struct_info_ext(_) -> erlang:error(function_clause)." << endl << endl;
+
+ generate_const_functions();
+
+ generate_type_metadata("struct_names", v_struct_names_);
+ generate_enum_metadata();
+ generate_type_metadata("enum_names", v_enum_names_);
+ generate_type_metadata("exception_names", v_exception_names_);
+
+ hrl_footer(f_types_hrl_file_, string("BOGUS"));
+
+ f_types_file_.close();
+ f_types_hrl_file_.close();
+ f_consts_file_.close();
+ f_consts_hrl_file_.close();
+}
+
+const std::string emit_double_as_string(const double value) {
+ std::stringstream double_output_stream;
+ // sets the maximum precision: http://en.cppreference.com/w/cpp/io/manip/setprecision
+ // sets the output format to fixed: http://en.cppreference.com/w/cpp/io/manip/fixed (not in scientific notation)
+ double_output_stream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
+
+ #ifdef _MSC_VER
+ // strtod is broken in MSVC compilers older than 2015, so std::fixed fails to format a double literal.
+ // more details: https://blogs.msdn.microsoft.com/vcblog/2014/06/18/
+ // c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/
+ // and
+ // http://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/
+ #if _MSC_VER >= MSC_2015_VER
+ double_output_stream << std::fixed;
+ #else
+ // note that if this function is called from the erlang generator and the MSVC compiler is older than 2015,
+ // the double literal must be output in the scientific format. There can be some cases where the
+ // mantissa of the output does not have fractionals, which is illegal in Erlang.
+ // example => 10000000000000000.0 being output as 1e+16
+ double_output_stream << std::scientific;
+ #endif
+ #else
+ double_output_stream << std::fixed;
+ #endif
+
+ double_output_stream << value;
+
+ return double_output_stream.str();
+}
+
+void t_erl_generator::generate_type_metadata(std::string function_name, vector<string> names) {
+ size_t num_structs = names.size();
+
+ indent(f_types_file_) << function_name << "() ->\n";
+ indent_up();
+ indent(f_types_file_) << "[";
+
+
+ for(size_t i=0; i < num_structs; i++) {
+ f_types_file_ << names.at(i);
+
+ if (i < num_structs - 1) {
+ f_types_file_ << ", ";
+ }
+ }
+
+ f_types_file_ << "].\n\n";
+ indent_down();
+}
+
+/**
+ * Generates a typedef. no op
+ *
+ * @param ttypedef The type definition
+ */
+void t_erl_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+
+void t_erl_generator::generate_const_function(t_const* tconst, ostringstream& exports, ostringstream& functions) {
+ t_type* type = get_true_type(tconst->get_type());
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ string const_fun_name = lowercase(name);
+
+ // Emit const function export.
+ if (exports.tellp() > 0) { exports << ", "; }
+ exports << const_fun_name << "/1, " << const_fun_name << "/2";
+
+ // Emit const function definition.
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator i, end = value->get_map().end();
+ // The one-argument form throws an error if the key does not exist in the map.
+ for (i = value->get_map().begin(); i != end;) {
+ functions << const_fun_name << "(" << render_const_value(ktype, i->first) << ") -> "
+ << render_const_value(vtype, i->second);
+ ++i;
+ functions << (i != end ? ";\n" : ".\n\n");
+ }
+
+ // The two-argument form returns a default value if the key does not exist in the map.
+ for (i = value->get_map().begin(); i != end; ++i) {
+ functions << const_fun_name << "(" << render_const_value(ktype, i->first) << ", _) -> "
+ << render_const_value(vtype, i->second) << ";\n";
+ }
+ functions << const_fun_name << "(_, Default) -> Default.\n\n";
+ } else if (type->is_list()) {
+ string const_fun_name = lowercase(name);
+
+ if (exports.tellp() > 0) { exports << ", "; }
+ exports << const_fun_name << "/1, " << const_fun_name << "/2";
+
+ size_t list_size = value->get_list().size();
+ string rendered_list = render_const_list_values(type, value);
+ functions << const_fun_name << "(N) when N >= 1, N =< " << list_size << " ->\n"
+ << indent_str() << "element(N, {" << rendered_list << "}).\n";
+ functions << const_fun_name << "(N, _) when N >= 1, N =< " << list_size << " ->\n"
+ << indent_str() << "element(N, {" << rendered_list << "});\n"
+ << const_fun_name << "(_, Default) -> Default.\n\n";
+ indent_down();
+ }
+}
+
+void t_erl_generator::generate_const_functions() {
+ ostringstream exports;
+ ostringstream functions;
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = v_consts_.begin(); c_iter != v_consts_.end(); ++c_iter) {
+ generate_const_function(*c_iter, exports, functions);
+ }
+ if (exports.tellp() > 0) {
+ f_consts_file_ << "-export([" << exports.str() << "]).\n\n"
+ << functions.str();
+ }
+}
+
+
+/**
+ * Generates code for an enumerated type. Done using a class to scope
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_erl_generator::generate_enum(t_enum* tenum) {
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ v_enums_.push_back(tenum);
+ v_enum_names_.push_back(atomify(tenum->get_name()));
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ string name = (*c_iter)->get_name();
+ indent(f_types_hrl_file_) << "-define(" << constify(make_safe_for_module_name(program_name_))
+ << "_" << constify(tenum->get_name()) << "_" << constify(name) << ", "
+ << value << ")." << endl;
+ }
+
+ f_types_hrl_file_ << endl;
+}
+
+void t_erl_generator::generate_enum_info(t_enum* tenum){
+ vector<t_enum_value*> constants = tenum->get_constants();
+ size_t num_constants = constants.size();
+
+ indent(f_types_file_) << "enum_info(" << atomify(tenum->get_name()) << ") ->\n";
+ indent_up();
+ indent(f_types_file_) << "[\n";
+
+ for(size_t i=0; i < num_constants; i++) {
+ indent_up();
+ t_enum_value* value = constants.at(i);
+ indent(f_types_file_) << "{" << atomify(value->get_name()) << ", " << value->get_value() << "}";
+
+ if (i < num_constants - 1) {
+ f_types_file_ << ",\n";
+ }
+ indent_down();
+ }
+ f_types_file_ << "\n";
+ indent(f_types_file_) << "];\n\n";
+ indent_down();
+}
+
+void t_erl_generator::generate_enum_metadata() {
+ size_t enum_count = v_enums_.size();
+
+ for(size_t i=0; i < enum_count; i++) {
+ t_enum* tenum = v_enums_.at(i);
+ generate_enum_info(tenum);
+ }
+
+ indent(f_types_file_) << "enum_info(_) -> erlang:error(function_clause).\n\n";
+}
+
+/**
+ * Generate a constant value
+ */
+void t_erl_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ // Save the tconst so that function can be emitted in generate_const_functions().
+ v_consts_.push_back(tconst);
+
+ f_consts_hrl_file_ << "-define(" << constify(make_safe_for_module_name(program_name_)) << "_"
+ << constify(name) << ", " << render_const_value(type, value) << ")." << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_erl_generator::render_const_value(t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream out;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << "float(" << value->get_integer() << ")";
+ } else {
+ out << emit_double_as_string(value->get_double());
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ indent(out) << value->get_integer();
+
+ } else if (type->is_struct() || type->is_xception()) {
+ out << "#" << type_name(type) << "{";
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ bool first = true;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ out << ",";
+ }
+ out << v_iter->first->get_string();
+ out << " = ";
+ out << render_const_value(field_type, v_iter->second);
+ }
+ indent_down();
+ indent(out) << "}";
+
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+
+ if (maps_) {
+ out << "maps:from_list([";
+ } else {
+ out << "dict:from_list([";
+ }
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator i, end = value->get_map().end();
+ for (i = value->get_map().begin(); i != end;) {
+ out << "{" << render_const_value(ktype, i->first) << ","
+ << render_const_value(vtype, i->second) << "}";
+ if (++i != end) {
+ out << ",";
+ }
+ }
+ out << "])";
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ out << "sets:from_list([";
+ vector<t_const_value*>::const_iterator i, end = value->get_list().end();
+ for (i = value->get_list().begin(); i != end;) {
+ out << render_const_value(etype, *i);
+ if (++i != end) {
+ out << ",";
+ }
+ }
+ out << "])";
+ } else if (type->is_list()) {
+ out << "[" << render_const_list_values(type, value) << "]";
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+ return out.str();
+}
+
+string t_erl_generator::render_const_list_values(t_type* type, t_const_value* value) {
+ std::ostringstream out;
+ t_type* etype = ((t_list*)type)->get_elem_type();
+
+ bool first = true;
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ",";
+ }
+ out << render_const_value(etype, *v_iter);
+ }
+ return out.str();
+}
+
+
+string t_erl_generator::render_default_value(t_field* field) {
+ t_type* type = field->get_type();
+ if (type->is_struct() || type->is_xception()) {
+ return "#" + type_name(type) + "{}";
+ } else if (type->is_map()) {
+ if (maps_) {
+ return "#{}";
+ } else {
+ return "dict:new()";
+ }
+ } else if (type->is_set()) {
+ return "sets:new()";
+ } else if (type->is_list()) {
+ return "[]";
+ } else {
+ return "undefined";
+ }
+}
+
+string t_erl_generator::render_member_type(t_field* field) {
+ t_type* type = get_true_type(field->get_type());
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ return "string() | binary()";
+ case t_base_type::TYPE_BOOL:
+ return "boolean()";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "integer()";
+ case t_base_type::TYPE_DOUBLE:
+ return "float()";
+ default:
+ throw "compiler error: unsupported base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ return "integer()";
+ } else if (type->is_struct() || type->is_xception()) {
+ return type_name(type) + "()";
+ } else if (type->is_map()) {
+ if (maps_) {
+ return "map()";
+ } else if (otp16_) {
+ return "dict()";
+ } else {
+ return "dict:dict()";
+ }
+ } else if (type->is_set()) {
+ if (otp16_) {
+ return "set()";
+ } else {
+ return "sets:set()";
+ }
+ } else if (type->is_list()) {
+ return "list()";
+ } else {
+ throw "compiler error: unsupported type " + type->get_name();
+ }
+}
+
+string t_erl_generator::render_member_requiredness(t_field* field) {
+ switch (field->get_req()) {
+ case t_field::T_REQUIRED:
+ return "required";
+ case t_field::T_OPTIONAL:
+ return "optional";
+ default:
+ return "undefined";
+ }
+}
+
+/**
+ * Generates a struct
+ */
+void t_erl_generator::generate_struct(t_struct* tstruct) {
+ v_struct_names_.push_back(type_name(tstruct));
+ generate_erl_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_erl_generator::generate_xception(t_struct* txception) {
+ v_exception_names_.push_back(type_name(txception));
+ generate_erl_struct(txception, true);
+}
+
+/**
+ * Generates a struct
+ */
+void t_erl_generator::generate_erl_struct(t_struct* tstruct, bool is_exception) {
+ (void)is_exception;
+ generate_erl_struct_definition(f_types_hrl_file_, tstruct);
+ generate_erl_struct_info(f_info_, tstruct);
+ generate_erl_extended_struct_info(f_info_ext_, tstruct);
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_erl_generator::generate_erl_struct_definition(ostream& out, t_struct* tstruct) {
+ indent(out) << "%% struct " << type_name(tstruct) << endl << endl;
+
+ std::stringstream buf;
+ buf << indent() << "-record(" << type_name(tstruct) << ", {";
+ string field_indent(buf.str().size(), ' ');
+
+ const vector<t_field*>& members = tstruct->get_members();
+ for (vector<t_field*>::const_iterator m_iter = members.begin(); m_iter != members.end();) {
+ generate_erl_struct_member(buf, *m_iter);
+ if (++m_iter != members.end()) {
+ buf << "," << endl << field_indent;
+ }
+ }
+ buf << "}).";
+
+ out << buf.str() << endl;
+ out << "-type " + type_name(tstruct) << "() :: #" + type_name(tstruct) + "{}." << endl << endl;
+}
+
+/**
+ * Generates the record field definition
+ */
+
+void t_erl_generator::generate_erl_struct_member(ostream& out, t_field* tmember) {
+ out << atomify(tmember->get_name());
+ if (has_default_value(tmember))
+ out << " = " << render_member_value(tmember);
+ out << " :: " << render_member_type(tmember);
+ if (tmember->get_req() != t_field::T_REQUIRED)
+ out << " | 'undefined'";
+}
+
+bool t_erl_generator::has_default_value(t_field* field) {
+ t_type* type = field->get_type();
+ if (!field->get_value()) {
+ if (field->get_req() == t_field::T_REQUIRED) {
+ if (type->is_struct() || type->is_xception() || type->is_map() || type->is_set()
+ || type->is_list()) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return true;
+ }
+}
+
+string t_erl_generator::render_member_value(t_field* field) {
+ if (!field->get_value()) {
+ return render_default_value(field);
+ } else {
+ return render_const_value(field->get_type(), field->get_value());
+ }
+}
+
+/**
+ * Generates the read method for a struct
+ */
+void t_erl_generator::generate_erl_struct_info(ostream& out, t_struct* tstruct) {
+ indent(out) << "struct_info(" << type_name(tstruct) << ") ->" << endl;
+ indent_up();
+ out << indent() << render_type_term(tstruct, true) << ";" << endl;
+ indent_down();
+ out << endl;
+}
+
+void t_erl_generator::generate_erl_extended_struct_info(ostream& out, t_struct* tstruct) {
+ indent(out) << "struct_info_ext(" << type_name(tstruct) << ") ->" << endl;
+ indent_up();
+ out << indent() << render_type_term(tstruct, true, true) << ";" << endl;
+ indent_down();
+ out << endl;
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_erl_generator::generate_service(t_service* tservice) {
+ service_name_ = make_safe_for_module_name(service_name_);
+
+ string f_service_hrl_name = get_out_dir() + service_name_ + "_thrift.hrl";
+ string f_service_name = get_out_dir() + service_name_ + "_thrift.erl";
+ f_service_file_.open(f_service_name.c_str());
+ f_service_hrl_.open(f_service_hrl_name.c_str());
+
+ // Reset service text aggregating stream streams
+ f_service_.str("");
+ export_lines_.str("");
+ export_lines_first_ = true;
+
+ hrl_header(f_service_hrl_, service_name_);
+
+ if (tservice->get_extends() != NULL) {
+ f_service_hrl_ << "-include(\""
+ << make_safe_for_module_name(tservice->get_extends()->get_name())
+ << "_thrift.hrl\"). % inherit " << endl;
+ }
+
+ f_service_hrl_ << "-include(\"" << make_safe_for_module_name(program_name_) << "_types.hrl\")."
+ << endl << endl;
+
+ // Generate the three main parts of the service (well, two for now in PHP)
+ generate_service_helpers(tservice); // cpiro: New Erlang Order
+
+ generate_service_interface(tservice);
+
+ generate_service_metadata(tservice);
+
+ // indent_down();
+
+ f_service_file_ << erl_autogen_comment() << endl << "-module(" << service_name_ << "_thrift)."
+ << endl << "-behaviour(thrift_service)." << endl << endl << erl_imports() << endl;
+
+ f_service_file_ << "-include(\"" << make_safe_for_module_name(tservice->get_name())
+ << "_thrift.hrl\")." << endl << endl;
+
+ f_service_file_ << "-export([" << export_lines_.str() << "])." << endl << endl;
+
+ f_service_file_ << f_service_.str();
+
+ hrl_footer(f_service_hrl_, f_service_name);
+
+ // Close service file
+ f_service_file_.close();
+ f_service_hrl_.close();
+}
+
+void t_erl_generator::generate_service_metadata(t_service* tservice) {
+ export_string("function_names", 0);
+ vector<t_function*> functions = tservice->get_functions();
+ size_t num_functions = functions.size();
+
+ indent(f_service_) << "function_names() -> " << endl;
+ indent_up();
+ indent(f_service_) << "[";
+
+ for (size_t i=0; i < num_functions; i++) {
+ t_function* current = functions.at(i);
+ f_service_ << atomify(current->get_name());
+ if (i < num_functions - 1) {
+ f_service_ << ", ";
+ }
+ }
+
+ f_service_ << "].\n\n";
+ indent_down();
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_erl_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // indent(f_service_) <<
+ // "% HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
+
+ export_string("struct_info", 1);
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_erl_function_helpers(*f_iter);
+ }
+ f_service_ << "struct_info(_) -> erlang:error(function_clause)." << endl;
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_erl_generator::generate_erl_function_helpers(t_function* tfunction) {
+ (void)tfunction;
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_erl_generator::generate_service_interface(t_service* tservice) {
+
+ export_string("function_info", 2);
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ f_service_ << "%%% interface" << endl;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "% " << function_signature(*f_iter) << endl;
+
+ generate_function_info(tservice, *f_iter);
+ }
+
+ // Inheritance - pass unknown functions to base class
+ if (tservice->get_extends() != NULL) {
+ indent(f_service_) << "function_info(Function, InfoType) ->" << endl;
+ indent_up();
+ indent(f_service_) << make_safe_for_module_name(tservice->get_extends()->get_name())
+ << "_thrift:function_info(Function, InfoType)." << endl;
+ indent_down();
+ } else {
+ // return function_clause error for non-existent functions
+ indent(f_service_) << "function_info(_Func, _Info) -> erlang:error(function_clause)." << endl;
+ }
+
+ indent(f_service_) << endl;
+}
+
+/**
+ * Generates a function_info(FunctionName, params_type) and
+ * function_info(FunctionName, reply_type)
+ */
+void t_erl_generator::generate_function_info(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ string name_atom = atomify(tfunction->get_name());
+
+ t_struct* xs = tfunction->get_xceptions();
+ t_struct* arg_struct = tfunction->get_arglist();
+
+ // function_info(Function, params_type):
+ indent(f_service_) << "function_info(" << name_atom << ", params_type) ->" << endl;
+ indent_up();
+
+ indent(f_service_) << render_type_term(arg_struct, true) << ";" << endl;
+
+ indent_down();
+
+ // function_info(Function, reply_type):
+ indent(f_service_) << "function_info(" << name_atom << ", reply_type) ->" << endl;
+ indent_up();
+
+ if (!tfunction->get_returntype()->is_void())
+ indent(f_service_) << render_type_term(tfunction->get_returntype(), false) << ";" << endl;
+ else if (tfunction->is_oneway())
+ indent(f_service_) << "oneway_void;" << endl;
+ else
+ indent(f_service_) << "{struct, []}"
+ << ";" << endl;
+ indent_down();
+
+ // function_info(Function, exceptions):
+ indent(f_service_) << "function_info(" << name_atom << ", exceptions) ->" << endl;
+ indent_up();
+ indent(f_service_) << render_type_term(xs, true) << ";" << endl;
+ indent_down();
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_erl_generator::function_signature(t_function* tfunction, string prefix) {
+ return prefix + tfunction->get_name() + "(This"
+ + capitalize(argument_list(tfunction->get_arglist())) + ")";
+}
+
+/**
+ * Add a function to the exports list
+ */
+void t_erl_generator::export_string(string name, int num) {
+ if (export_lines_first_) {
+ export_lines_first_ = false;
+ } else {
+ export_lines_ << ", ";
+ }
+ export_lines_ << name << "/" << num;
+}
+
+void t_erl_generator::export_types_string(string name, int num) {
+ if (export_types_lines_first_) {
+ export_types_lines_first_ = false;
+ } else {
+ export_types_lines_ << ", ";
+ }
+ export_types_lines_ << name << "/" << num;
+}
+
+void t_erl_generator::export_function(t_function* tfunction, string prefix) {
+ t_struct::members_type::size_type num = tfunction->get_arglist()->get_members().size();
+ if (num > static_cast<t_struct::members_type::size_type>(std::numeric_limits<int>().max())) {
+ throw "integer overflow in t_erl_generator::export_function, name " + tfunction->get_name();
+ }
+ export_string(prefix + tfunction->get_name(),
+ 1 // This
+ + static_cast<int>(num));
+}
+
+/**
+ * Renders a field list
+ */
+string t_erl_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ result += ", "; // initial comma to compensate for initial This
+ } else {
+ result += ", ";
+ }
+ result += capitalize((*f_iter)->get_name());
+ }
+ return result;
+}
+
+string t_erl_generator::type_name(t_type* ttype) {
+ string prefix = ttype->get_program()->get_namespace("erl");
+ size_t prefix_length = prefix.length();
+ if (prefix_length > 0 && prefix[prefix_length - 1] != '_') {
+ prefix += '.';
+ }
+
+ string name = ttype->get_name();
+
+ return atomify(prefix + name);
+}
+
+/**
+ * Converts the parse type to a Erlang "type" (macro for int constants)
+ */
+string t_erl_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "?tType_STRING";
+ case t_base_type::TYPE_BOOL:
+ return "?tType_BOOL";
+ case t_base_type::TYPE_I8:
+ return "?tType_I8";
+ case t_base_type::TYPE_I16:
+ return "?tType_I16";
+ case t_base_type::TYPE_I32:
+ return "?tType_I32";
+ case t_base_type::TYPE_I64:
+ return "?tType_I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "?tType_DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "?tType_I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "?tType_STRUCT";
+ } else if (type->is_map()) {
+ return "?tType_MAP";
+ } else if (type->is_set()) {
+ return "?tType_SET";
+ } else if (type->is_list()) {
+ return "?tType_LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Generate an Erlang term which represents a thrift type
+ */
+std::string t_erl_generator::render_type_term(t_type* type,
+ bool expand_structs,
+ bool extended_info) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "string";
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "byte";
+ case t_base_type::TYPE_I16:
+ return "i16";
+ case t_base_type::TYPE_I32:
+ return "i32";
+ case t_base_type::TYPE_I64:
+ return "i64";
+ case t_base_type::TYPE_DOUBLE:
+ return "double";
+ }
+ } else if (type->is_enum()) {
+ return "i32";
+ } else if (type->is_struct() || type->is_xception()) {
+ if (expand_structs) {
+
+ std::stringstream buf;
+ buf << "{struct, [";
+ string field_indent(buf.str().size(), ' ');
+
+ t_struct::members_type const& fields = static_cast<t_struct*>(type)->get_members();
+ t_struct::members_type::const_iterator i, end = fields.end();
+ for (i = fields.begin(); i != end;) {
+ t_struct::members_type::value_type member = *i;
+ int32_t key = member->get_key();
+ string type = render_type_term(member->get_type(), false, false); // recursive call
+
+ if (!extended_info) {
+ // Convert to format: {struct, [{Fid, Type}|...]}
+ buf << "{" << key << ", " << type << "}";
+ } else {
+ // Convert to format: {struct, [{Fid, Req, Type, Name, Def}|...]}
+ string name = member->get_name();
+ string value = render_member_value(member);
+ string requiredness = render_member_requiredness(member);
+ buf << "{" << key << ", " << requiredness << ", " << type << ", " << atomify(name) << ", "
+ << value << "}";
+ }
+
+ if (++i != end) {
+ buf << "," << endl << field_indent;
+ }
+ }
+
+ buf << "]}" << endl;
+ return buf.str();
+ } else {
+ return "{struct, {" + atomify(type_module(type)) + ", " + type_name(type) + "}}";
+ }
+ } else if (type->is_map()) {
+ // {map, KeyType, ValType}
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+
+ return "{map, " + render_type_term(key_type, false) + ", " + render_type_term(val_type, false)
+ + "}";
+
+ } else if (type->is_set()) {
+ t_type* elem_type = ((t_set*)type)->get_elem_type();
+
+ return "{set, " + render_type_term(elem_type, false) + "}";
+
+ } else if (type->is_list()) {
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+
+ return "{list, " + render_type_term(elem_type, false) + "}";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+std::string t_erl_generator::type_module(t_type* ttype) {
+ return make_safe_for_module_name(ttype->get_program()->get_name()) + "_types";
+}
+
+THRIFT_REGISTER_GENERATOR(
+ erl,
+ "Erlang",
+ " legacynames: Output files retain naming conventions of Thrift 0.9.1 and earlier.\n"
+ " maps: Generate maps instead of dicts.\n"
+ " otp16: Generate non-namespaced dict and set instead of dict:dict and sets:set.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.cc
new file mode 100644
index 000000000..ca3f5dd68
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.cc
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include "thrift/generate/t_generator.h"
+using namespace std;
+
+/**
+ * Top level program generation function. Calls the generator subclass methods
+ * for preparing file streams etc. then iterates over all the parts of the
+ * program to perform the correct actions.
+ *
+ * @param program The thrift program to compile into C++ source
+ */
+void t_generator::generate_program() {
+ // Initialize the generator
+ init_generator();
+
+ // Generate enums
+ vector<t_enum*> enums = program_->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ generate_enum(*en_iter);
+ }
+
+ // Generate typedefs
+ vector<t_typedef*> typedefs = program_->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ generate_typedef(*td_iter);
+ }
+
+ // Generate structs, exceptions, and unions in declared order
+ vector<t_struct*> objects = program_->get_objects();
+
+ vector<t_struct*>::iterator o_iter;
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ generate_forward_declaration(*o_iter);
+ }
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ if ((*o_iter)->is_xception()) {
+ generate_xception(*o_iter);
+ } else {
+ generate_struct(*o_iter);
+ }
+ }
+
+ // Generate constants
+ vector<t_const*> consts = program_->get_consts();
+ generate_consts(consts);
+
+ // Generate services
+ vector<t_service*> services = program_->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ service_name_ = get_service_name(*sv_iter);
+ generate_service(*sv_iter);
+ }
+
+ // Close the generator
+ close_generator();
+}
+
+std::set<std::string> t_generator::lang_keywords() const {
+ std::string keywords[] = { "BEGIN", "END", "__CLASS__", "__DIR__", "__FILE__", "__FUNCTION__",
+ "__LINE__", "__METHOD__", "__NAMESPACE__", "abstract", "alias", "and", "args", "as",
+ "assert", "begin", "break", "case", "catch", "class", "clone", "continue", "declare",
+ "def", "default", "del", "delete", "do", "dynamic", "elif", "else", "elseif", "elsif",
+ "end", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "ensure",
+ "except", "exec", "finally", "float", "for", "foreach", "from", "function", "global",
+ "goto", "if", "implements", "import", "in", "inline", "instanceof", "interface", "is",
+ "lambda", "module", "native", "new", "next", "nil", "not", "or", "package", "pass",
+ "public", "print", "private", "protected", "raise", "redo", "rescue", "retry", "register",
+ "return", "self", "sizeof", "static", "super", "switch", "synchronized", "then", "this",
+ "throw", "transient", "try", "undef", "unless", "unsigned", "until", "use", "var",
+ "virtual", "volatile", "when", "while", "with", "xor", "yield" };
+ return std::set<std::string>(keywords, keywords + sizeof(keywords)/sizeof(keywords[0]) );
+}
+
+void t_generator::validate_input() const {
+ validate(program_->get_enums());
+ validate(program_->get_typedefs());
+ validate(program_->get_objects());
+ validate(program_->get_consts());
+ validate(program_->get_services());
+}
+
+template <typename T>
+void t_generator::validate(const vector<T>& list) const{
+ typename vector<T>::const_iterator it;
+ for(it=list.begin(); it != list.end(); ++it) {
+ validate(*it);
+ }
+}
+
+void t_generator::validate(t_function const* f) const {
+ validate_id(f->get_name());
+ validate(f->get_arglist());
+ validate(f->get_xceptions());
+}
+
+void t_generator::validate(t_service const* s) const {
+ validate_id(s->get_name());
+ validate(s->get_functions());
+}
+
+void t_generator::validate(t_enum const* en) const {
+ validate_id(en->get_name());
+ validate(en->get_constants());
+}
+void t_generator::validate(t_struct const* s) const {
+ validate_id(s->get_name());
+ validate(s->get_members());
+}
+
+void t_generator::validate(t_enum_value const* en_val) const {
+ validate_id(en_val->get_name());
+}
+void t_generator::validate(t_typedef const* td) const {
+ validate_id(td->get_name());
+}
+void t_generator::validate(t_const const* c) const {
+ validate_id(c->get_name());
+}
+void t_generator::validate(t_field const* f) const {
+ validate_id(f->get_name());
+}
+
+void t_generator::validate_id(const string& id) const {
+ if (keywords_.find(id) != keywords_.end()) {
+ failure("Cannot use reserved language keyword: \"%s\"", id.c_str());
+ }
+}
+
+string t_generator::escape_string(const string& in) const {
+ string result = "";
+ for (string::const_iterator it = in.begin(); it < in.end(); it++) {
+ std::map<char, std::string>::const_iterator res = escape_.find(*it);
+ if (res != escape_.end()) {
+ result.append(res->second);
+ } else {
+ result.push_back(*it);
+ }
+ }
+ return result;
+}
+
+void t_generator::generate_consts(vector<t_const*> consts) {
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ generate_const(*c_iter);
+ }
+}
+
+void t_generator::generate_docstring_comment(ostream& out,
+ const string& comment_start,
+ const string& line_prefix,
+ const string& contents,
+ const string& comment_end) {
+ if (!comment_start.empty())
+ indent(out) << comment_start;
+ stringstream docs(contents, ios_base::in);
+ while (!(docs.eof() || docs.fail())) {
+ char line[1024];
+ docs.getline(line, 1024);
+
+ if (strlen(line) > 0) {
+ indent(out) << line_prefix << line << std::endl;
+ } else if (line_prefix.empty()){
+ out << std::endl;
+ } else if(!docs.eof()) {
+ indent(out) << line_prefix << std::endl;
+ }
+ }
+ if (!comment_end.empty())
+ indent(out) << comment_end;
+}
+
+void t_generator_registry::register_generator(t_generator_factory* factory) {
+ gen_map_t& the_map = get_generator_map();
+ if (the_map.find(factory->get_short_name()) != the_map.end()) {
+ failure("Duplicate generators for language \"%s\"!\n", factory->get_short_name().c_str());
+ }
+ the_map[factory->get_short_name()] = factory;
+}
+
+void t_generator::parse_options(const string& options,
+ string& language,
+ map<string, string>& parsed_options) {
+ string::size_type colon = options.find(':');
+ language = options.substr(0, colon);
+
+ if (colon != string::npos) {
+ string::size_type pos = colon + 1;
+ while (pos != string::npos && pos < options.size()) {
+ string::size_type next_pos = options.find(',', pos);
+ string option = options.substr(pos, next_pos - pos);
+ pos = ((next_pos == string::npos) ? next_pos : next_pos + 1);
+
+ string::size_type separator = option.find('=');
+ string key, value;
+ if (separator == string::npos) {
+ key = option;
+ value = "";
+ } else {
+ key = option.substr(0, separator);
+ value = option.substr(separator + 1);
+ }
+
+ parsed_options[key] = value;
+ }
+ }
+}
+
+t_generator* t_generator_registry::get_generator(t_program* program,
+ const string& language,
+ const map<string, string>& parsed_options,
+ const std::string& options) {
+ gen_map_t& the_map = get_generator_map();
+ gen_map_t::iterator iter = the_map.find(language);
+
+ if (iter == the_map.end()) {
+ return NULL;
+ }
+
+ return iter->second->get_generator(program, parsed_options, options);
+}
+
+t_generator* t_generator_registry::get_generator(t_program* program, const string& options) {
+ string language;
+ map<string, string> parsed_options;
+ t_generator::parse_options(options, language, parsed_options);
+ return get_generator(program, language, parsed_options, options);
+}
+
+t_generator_registry::gen_map_t& t_generator_registry::get_generator_map() {
+ // http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12
+ static gen_map_t* the_map = new gen_map_t();
+ return *the_map;
+}
+
+t_generator_factory::t_generator_factory(const std::string& short_name,
+ const std::string& long_name,
+ const std::string& documentation)
+ : short_name_(short_name), long_name_(long_name), documentation_(documentation) {
+ t_generator_registry::register_generator(this);
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.h
new file mode 100644
index 000000000..cb9d076b5
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator.h
@@ -0,0 +1,452 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_GENERATOR_H
+#define T_GENERATOR_H
+#define MSC_2015_VER 1900
+
+#include <cstring>
+#include <string>
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+#include <limits>
+#include <sstream>
+#include "thrift/common.h"
+#include "thrift/logging.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_generator_registry.h"
+#include "thrift/parse/t_program.h"
+
+/**
+ * Base class for a thrift code generator. This class defines the basic
+ * routines for code generation and contains the top level method that
+ * dispatches code generation across various components.
+ *
+ */
+class t_generator {
+public:
+ t_generator(t_program* program)
+ : keywords_(lang_keywords()){
+ tmp_ = 0;
+ indent_ = 0;
+ program_ = program;
+ program_name_ = get_program_name(program);
+ escape_['\n'] = "\\n";
+ escape_['\r'] = "\\r";
+ escape_['\t'] = "\\t";
+ escape_['"'] = "\\\"";
+ escape_['\\'] = "\\\\";
+ }
+
+ virtual ~t_generator() {}
+
+ /**
+ * Framework generator method that iterates over all the parts of a program
+ * and performs general actions. This is implemented by the base class and
+ * should not normally be overwritten in the subclasses.
+ */
+ virtual void generate_program();
+
+ const t_program* get_program() const { return program_; }
+
+ void generate_docstring_comment(std::ostream& out,
+ const std::string& comment_start,
+ const std::string& line_prefix,
+ const std::string& contents,
+ const std::string& comment_end);
+
+ static void parse_options(const std::string& options, std::string& language,
+ std::map<std::string, std::string>& parsed_options);
+
+ /**
+ * check whether sub-namespace declaraction is used by generator.
+ * e.g. allow
+ * namespace py.twisted bar
+ * to specify namespace to use when -gen py:twisted is specified.
+ * Will be called with subnamespace, i.e. is_valid_namespace("twisted")
+ * will be called for the above example.
+ */
+ static bool is_valid_namespace(const std::string& sub_namespace) {
+ (void)sub_namespace;
+ return false;
+ }
+
+ /**
+ * Escape string to use one in generated sources.
+ */
+ virtual std::string escape_string(const std::string& in) const;
+
+ std::string get_escaped_string(t_const_value* constval) {
+ return escape_string(constval->get_string());
+ }
+
+ /**
+ * Check if all identifiers are valid for the target language
+ */
+ virtual void validate_input() const;
+
+protected:
+ virtual std::set<std::string> lang_keywords() const;
+
+ /**
+ * A list of reserved words that cannot be used as identifiers.
+ */
+ const std::set<std::string> keywords_;
+
+ virtual void validate_id(const std::string& id) const;
+
+ virtual void validate(t_enum const* en) const;
+ virtual void validate(t_enum_value const* en_val) const;
+ virtual void validate(t_typedef const* td) const;
+ virtual void validate(t_const const* c) const;
+ virtual void validate(t_service const* s) const;
+ virtual void validate(t_struct const* c) const;
+ virtual void validate(t_field const* f) const;
+ virtual void validate(t_function const* f) const;
+
+ template <typename T>
+ void validate(const std::vector<T>& list) const;
+
+ /**
+ * Optional methods that may be implemented by subclasses to take necessary
+ * steps at the beginning or end of code generation.
+ */
+
+ virtual void init_generator() {}
+ virtual void close_generator() {}
+
+ virtual void generate_consts(std::vector<t_const*> consts);
+
+ /**
+ * Pure virtual methods implemented by the generator subclasses.
+ */
+
+ virtual void generate_typedef(t_typedef* ttypedef) = 0;
+ virtual void generate_enum(t_enum* tenum) = 0;
+ virtual void generate_const(t_const* tconst) { (void)tconst; }
+ virtual void generate_struct(t_struct* tstruct) = 0;
+ virtual void generate_service(t_service* tservice) = 0;
+ virtual void generate_forward_declaration(t_struct*) {}
+ virtual void generate_xception(t_struct* txception) {
+ // By default exceptions are the same as structs
+ generate_struct(txception);
+ }
+
+ /**
+ * Method to get the program name, may be overridden
+ */
+ virtual std::string get_program_name(t_program* tprogram) { return tprogram->get_name(); }
+
+ /**
+ * Method to get the service name, may be overridden
+ */
+ virtual std::string get_service_name(t_service* tservice) { return tservice->get_name(); }
+
+ /**
+ * Get the current output directory
+ */
+ virtual std::string get_out_dir() const {
+ if (program_->is_out_path_absolute()) {
+ return program_->get_out_path() + "/";
+ }
+
+ return program_->get_out_path() + out_dir_base_ + "/";
+ }
+
+ /**
+ * Creates a unique temporary variable name, which is just "name" with a
+ * number appended to it (i.e. name35)
+ */
+ std::string tmp(std::string name) {
+ std::ostringstream out;
+ out << name << tmp_++;
+ return out.str();
+ }
+
+ /**
+ * Generates a comment about this code being autogenerated, using C++ style
+ * comments, which are also fair game in Java / PHP, yay!
+ *
+ * @return C-style comment mentioning that this file is autogenerated.
+ */
+ virtual std::string autogen_comment() {
+ return std::string("/**\n") + " * " + autogen_summary() + "\n" + " *\n"
+ + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
+ + " * @generated\n" + " */\n";
+ }
+
+ virtual std::string autogen_summary() {
+ return std::string("Autogenerated by Thrift Compiler (") + THRIFT_VERSION + ")";
+ }
+
+ /**
+ * Indentation level modifiers
+ */
+
+ void indent_up() { ++indent_; }
+
+ void indent_down() { --indent_; }
+
+ /**
+ * Indentation validation helper
+ */
+ int indent_count() { return indent_; }
+
+ void indent_validate( int expected, const char * func_name) {
+ if (indent_ != expected) {
+ pverbose("Wrong indent count in %s: difference = %i \n", func_name, (expected - indent_));
+ }
+ }
+
+ /**
+ * Indentation print function
+ */
+ std::string indent() {
+ std::string ind = "";
+ int i;
+ for (i = 0; i < indent_; ++i) {
+ ind += indent_str();
+ }
+ return ind;
+ }
+
+ /**
+ * Indentation utility wrapper
+ */
+ std::ostream& indent(std::ostream& os) { return os << indent(); }
+
+ /**
+ * Capitalization helpers
+ */
+ std::string capitalize(std::string in) {
+ in[0] = toupper(in[0]);
+ return in;
+ }
+ std::string decapitalize(std::string in) {
+ in[0] = tolower(in[0]);
+ return in;
+ }
+ static std::string lowercase(std::string in) {
+ for (size_t i = 0; i < in.size(); ++i) {
+ in[i] = tolower(in[i]);
+ }
+ return in;
+ }
+ static std::string uppercase(std::string in) {
+ for (size_t i = 0; i < in.size(); ++i) {
+ in[i] = toupper(in[i]);
+ }
+ return in;
+ }
+
+ /**
+ * Transforms a camel case string to an equivalent one separated by underscores
+ * e.g. aMultiWord -> a_multi_word
+ * someName -> some_name
+ * CamelCase -> camel_case
+ * name -> name
+ * Name -> name
+ */
+ std::string underscore(std::string in) {
+ in[0] = tolower(in[0]);
+ for (size_t i = 1; i < in.size(); ++i) {
+ if (isupper(in[i])) {
+ in[i] = tolower(in[i]);
+ in.insert(i, "_");
+ }
+ }
+ return in;
+ }
+
+ /**
+ * Transforms a string with words separated by underscores to a camel case equivalent
+ * e.g. a_multi_word -> aMultiWord
+ * some_name -> someName
+ * name -> name
+ */
+ std::string camelcase(std::string in) {
+ std::ostringstream out;
+ bool underscore = false;
+
+ for (size_t i = 0; i < in.size(); i++) {
+ if (in[i] == '_') {
+ underscore = true;
+ continue;
+ }
+ if (underscore) {
+ out << (char)toupper(in[i]);
+ underscore = false;
+ continue;
+ }
+ out << in[i];
+ }
+
+ return out.str();
+ }
+
+ const std::string emit_double_as_string(const double value) {
+ std::stringstream double_output_stream;
+ // sets the maximum precision: http://en.cppreference.com/w/cpp/io/manip/setprecision
+ // sets the output format to fixed: http://en.cppreference.com/w/cpp/io/manip/fixed (not in scientific notation)
+ double_output_stream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
+
+ #ifdef _MSC_VER
+ // strtod is broken in MSVC compilers older than 2015, so std::fixed fails to format a double literal.
+ // more details: https://blogs.msdn.microsoft.com/vcblog/2014/06/18/
+ // c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/
+ // and
+ // http://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/
+ #if _MSC_VER >= MSC_2015_VER
+ double_output_stream << std::fixed;
+ #endif
+ #else
+ double_output_stream << std::fixed;
+ #endif
+
+ double_output_stream << value;
+
+ return double_output_stream.str();
+ }
+
+public:
+ /**
+ * Get the true type behind a series of typedefs.
+ */
+ static const t_type* get_true_type(const t_type* type) { return type->get_true_type(); }
+ static t_type* get_true_type(t_type* type) { return type->get_true_type(); }
+
+protected:
+ /**
+ * The program being generated
+ */
+ t_program* program_;
+
+ /**
+ * Quick accessor for formatted program name that is currently being
+ * generated.
+ */
+ std::string program_name_;
+
+ /**
+ * Quick accessor for formatted service name that is currently being
+ * generated.
+ */
+ std::string service_name_;
+
+ /**
+ * Output type-specifc directory name ("gen-*")
+ */
+ std::string out_dir_base_;
+
+ /**
+ * Map of characters to escape in string literals.
+ */
+ std::map<char, std::string> escape_;
+
+ virtual std::string indent_str() const {
+ return " ";
+ }
+
+private:
+ /**
+ * Current code indentation level
+ */
+ int indent_;
+
+ /**
+ * Temporary variable counter, for making unique variable names
+ */
+ int tmp_;
+};
+
+template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
+class template_ofstream_with_content_based_conditional_update : public std::ostringstream {
+public:
+ template_ofstream_with_content_based_conditional_update(): contents_written(false) {}
+
+ template_ofstream_with_content_based_conditional_update(std::string const& output_file_path_)
+ : output_file_path(output_file_path_), contents_written(false) {}
+
+ ~template_ofstream_with_content_based_conditional_update() {
+ if (!contents_written) {
+ close();
+ }
+ }
+
+ void open(std::string const& output_file_path_) {
+ output_file_path = output_file_path_;
+ clear_buf();
+ contents_written = false;
+ }
+
+ void close() {
+ if (contents_written || output_file_path == "")
+ return;
+
+ if (!is_readable(output_file_path)) {
+ dump();
+ return;
+ }
+
+ std::ifstream old_file;
+ old_file.exceptions(old_file.exceptions() | std::ifstream::badbit | std::ifstream::failbit);
+ old_file.open(output_file_path.c_str(), std::ios::in);
+
+ if (old_file) {
+ std::string const old_file_contents(static_cast<std::ostringstream const&>(std::ostringstream() << old_file.rdbuf()).str());
+ old_file.close();
+
+ if (old_file_contents != str()) {
+ dump();
+ }
+ }
+ }
+
+protected:
+ void dump() {
+ std::ofstream out_file;
+ out_file.exceptions(out_file.exceptions() | std::ofstream::badbit | std::ofstream::failbit);
+ try {
+ out_file.open(output_file_path.c_str(), std::ios::out);
+ }
+ catch (const std::ios_base::failure& e) {
+ ::failure("failed to write the output to the file '%s', details: '%s'", output_file_path.c_str(), e.what());
+ }
+ out_file << str();
+ out_file.close();
+ clear_buf();
+ contents_written = true;
+ }
+
+ void clear_buf() {
+ str(std::string());
+ }
+
+ static bool is_readable(std::string const& file_name) {
+ return static_cast<bool>(std::ifstream(file_name.c_str()));
+ }
+
+private:
+ std::string output_file_path;
+ bool contents_written;
+};
+typedef template_ofstream_with_content_based_conditional_update<char> ofstream_with_content_based_conditional_update;
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h
new file mode 100644
index 000000000..d27f71082
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_GENERATOR_REGISTRY_H
+#define T_GENERATOR_REGISTRY_H
+
+class t_generator;
+
+/**
+ * A factory for producing generator classes of a particular language.
+ *
+ * This class is also responsible for:
+ * - Registering itself with the generator registry.
+ * - Providing documentation for the generators it produces.
+ */
+class t_generator_factory {
+public:
+ t_generator_factory(const std::string& short_name,
+ const std::string& long_name,
+ const std::string& documentation);
+
+ virtual ~t_generator_factory() = default;
+
+ virtual t_generator* get_generator(
+ // The program to generate.
+ t_program* program,
+ // Note: parsed_options will not exist beyond the call to get_generator.
+ const std::map<std::string, std::string>& parsed_options,
+ // Note: option_string might not exist beyond the call to get_generator.
+ const std::string& option_string) = 0;
+
+ virtual bool is_valid_namespace(const std::string& sub_namespace) = 0;
+
+ std::string get_short_name() { return short_name_; }
+ std::string get_long_name() { return long_name_; }
+ std::string get_documentation() { return documentation_; }
+
+private:
+ std::string short_name_;
+ std::string long_name_;
+ std::string documentation_;
+};
+
+template <typename generator>
+class t_generator_factory_impl : public t_generator_factory {
+public:
+ t_generator_factory_impl(const std::string& short_name,
+ const std::string& long_name,
+ const std::string& documentation)
+ : t_generator_factory(short_name, long_name, documentation) {}
+
+ t_generator* get_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string) override {
+ return new generator(program, parsed_options, option_string);
+ }
+
+ bool is_valid_namespace(const std::string& sub_namespace) override {
+ return generator::is_valid_namespace(sub_namespace);
+ }
+};
+
+class t_generator_registry {
+public:
+ static void register_generator(t_generator_factory* factory);
+
+ static t_generator* get_generator(t_program* program, const std::string& options);
+ static t_generator* get_generator(t_program* program,
+ const std::string& laugnage,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& options);
+
+ typedef std::map<std::string, t_generator_factory*> gen_map_t;
+ static gen_map_t& get_generator_map();
+
+private:
+ t_generator_registry();
+ t_generator_registry(const t_generator_registry&);
+};
+
+#define THRIFT_REGISTER_GENERATOR(language, long_name, doc) \
+ class t_##language##_generator_factory_impl \
+ : public t_generator_factory_impl<t_##language##_generator> { \
+ public: \
+ t_##language##_generator_factory_impl() \
+ : t_generator_factory_impl<t_##language##_generator>(#language, long_name, doc) {} \
+ }; \
+ static t_##language##_generator_factory_impl _registerer;
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc
new file mode 100644
index 000000000..33b7547b4
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc
@@ -0,0 +1,3742 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * This file is programmatically sanitized for style:
+ * astyle --style=1tbs -f -p -H -j -U t_go_generator.cc
+ *
+ * The output of astyle should not be taken unquestioningly, but it is a good
+ * guide for ensuring uniformity and readability.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include <algorithm>
+#include <clocale>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * A helper for automatically formatting the emitted Go code from the Thrift
+ * IDL per the Go style guide.
+ *
+ * Returns:
+ * - true, if the formatting process succeeded.
+ * - false, if the formatting process failed, which means the basic output was
+ * still generated.
+ */
+bool format_go_output(const string& file_path);
+
+const string DEFAULT_THRIFT_IMPORT = "github.com/apache/thrift/lib/go/thrift";
+static std::string package_flag;
+
+/**
+ * Go code generator.
+ */
+class t_go_generator : public t_generator {
+public:
+ t_go_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+
+ gen_thrift_import_ = DEFAULT_THRIFT_IMPORT;
+ gen_package_prefix_ = "";
+ package_flag = "";
+ read_write_private_ = false;
+ ignore_initialisms_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("package_prefix") == 0) {
+ gen_package_prefix_ = (iter->second);
+ } else if( iter->first.compare("thrift_import") == 0) {
+ gen_thrift_import_ = (iter->second);
+ } else if( iter->first.compare("package") == 0) {
+ package_flag = (iter->second);
+ } else if( iter->first.compare("read_write_private") == 0) {
+ read_write_private_ = true;
+ } else if( iter->first.compare("ignore_initialisms") == 0) {
+ ignore_initialisms_ = true;
+ } else {
+ throw "unknown option go:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-go";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_const_value(t_type* type, t_const_value* value, const string& name, bool opt = false);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_go_struct(t_struct* tstruct, bool is_exception);
+ void generate_go_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool is_result = false,
+ bool is_args = false);
+ void generate_go_struct_initializer(std::ostream& out,
+ t_struct* tstruct,
+ bool is_args_or_result = false);
+ void generate_isset_helpers(std::ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result = false);
+ void generate_countsetfields_helper(std::ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result = false);
+ void generate_go_struct_reader(std::ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result = false);
+ void generate_go_struct_writer(std::ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result = false,
+ bool uses_countsetfields = false);
+ void generate_go_function_helpers(t_function* tfunction);
+ void get_publicized_name_and_def_value(t_field* tfield,
+ string* OUT_pub_name,
+ t_const_value** OUT_def_value) const;
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_remote(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ bool declare,
+ std::string prefix = "",
+ bool inclass = false,
+ bool coerceData = false,
+ bool inkey = false,
+ bool in_container = false);
+
+ void generate_deserialize_struct(std::ostream& out,
+ t_struct* tstruct,
+ bool is_pointer_field,
+ bool declare,
+ std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out,
+ t_type* ttype,
+ bool pointer_field,
+ bool declare,
+ std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out,
+ t_set* tset,
+ bool declare,
+ std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out,
+ t_map* tmap,
+ bool declare,
+ std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ bool declare,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool inkey = false);
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out,
+ t_type* ttype,
+ bool pointer_field,
+ std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_go_docstring(std::ostream& out, t_struct* tstruct);
+
+ void generate_go_docstring(std::ostream& out, t_function* tfunction);
+
+ void generate_go_docstring(std::ostream& out,
+ t_doc* tdoc,
+ t_struct* tstruct,
+ const char* subheader);
+
+ void generate_go_docstring(std::ostream& out, t_doc* tdoc);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string go_autogen_comment();
+ std::string go_package();
+ std::string go_imports_begin(bool consts);
+ std::string go_imports_end();
+ std::string render_includes(bool consts);
+ std::string render_included_programs(string& unused_protection);
+ std::string render_program_import(const t_program* program, string& unused_protection);
+ std::string render_system_packages(std::vector<string> &system_packages);
+ std::string render_import_protection();
+ std::string render_fastbinary_includes();
+ std::string declare_argument(t_field* tfield);
+ std::string render_field_initial_value(t_field* tfield, const string& name, bool optional_field);
+ std::string type_name(t_type* ttype);
+ std::string module_name(t_type* ttype);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string function_signature_if(t_function* tfunction,
+ std::string prefix = "",
+ bool addError = false);
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string type_to_go_type(t_type* ttype);
+ std::string type_to_go_type_with_opt(t_type* ttype,
+ bool optional_field);
+ std::string type_to_go_key_type(t_type* ttype);
+ std::string type_to_spec_args(t_type* ttype);
+
+ static std::string get_real_go_module(const t_program* program) {
+
+ if (!package_flag.empty()) {
+ return package_flag;
+ }
+ std::string real_module = program->get_namespace("go");
+ if (!real_module.empty()) {
+ return real_module;
+ }
+
+ return lowercase(program->get_name());
+ }
+
+private:
+ std::string gen_package_prefix_;
+ std::string gen_thrift_import_;
+ bool read_write_private_;
+ bool ignore_initialisms_;
+
+ /**
+ * File streams
+ */
+
+ ofstream_with_content_based_conditional_update f_types_;
+ std::string f_types_name_;
+ ofstream_with_content_based_conditional_update f_consts_;
+ std::string f_consts_name_;
+ std::stringstream f_const_values_;
+
+ std::string package_name_;
+ std::string package_dir_;
+ std::unordered_map<std::string, std::string> package_identifiers_;
+ std::set<std::string> package_identifiers_set_;
+ std::string read_method_name_;
+ std::string write_method_name_;
+
+ std::set<std::string> commonInitialisms;
+
+ std::string camelcase(const std::string& value) const;
+ void fix_common_initialism(std::string& value, int i) const;
+ std::string publicize(const std::string& value, bool is_args_or_result = false) const;
+ std::string privatize(const std::string& value) const;
+ std::string new_prefix(const std::string& value) const;
+ static std::string variable_name_to_go_name(const std::string& value);
+ static bool is_pointer_field(t_field* tfield, bool in_container = false);
+ static bool omit_initialization(t_field* tfield);
+};
+
+// returns true if field initialization can be omitted since it has corresponding go type zero value
+// or default value is not set
+bool t_go_generator::omit_initialization(t_field* tfield) {
+ t_const_value* value = tfield->get_value();
+ if (!value) {
+ return true;
+ }
+ t_type* type = tfield->get_type()->get_true_type();
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "";
+
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ //[]byte are always inline
+ return false;
+ }
+ // strings are pointers if has no default
+ return value->get_string().empty();
+
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return value->get_integer() == 0;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ return value->get_integer() == 0;
+ } else {
+ return value->get_double() == 0.;
+ }
+ }
+ }
+ return false;
+}
+
+// Returns true if the type need a reference if used as optional without default
+static bool type_need_reference(t_type* type) {
+ type = type->get_true_type();
+ if (type->is_map() || type->is_set() || type->is_list() || type->is_struct()
+ || type->is_xception() || type->is_binary()) {
+ return false;
+ }
+ return true;
+}
+
+// returns false if field could not use comparison to default value as !IsSet*
+bool t_go_generator::is_pointer_field(t_field* tfield, bool in_container_value) {
+ (void)in_container_value;
+ if (tfield->annotations_.count("cpp.ref") != 0) {
+ return true;
+ }
+ t_type* type = tfield->get_type()->get_true_type();
+ // Structs in containers are pointers
+ if (type->is_struct() || type->is_xception()) {
+ return true;
+ }
+ if (!(tfield->get_req() == t_field::T_OPTIONAL)) {
+ return false;
+ }
+ bool has_default = tfield->get_value();
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "";
+
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ //[]byte are always inline
+ return false;
+ }
+ // strings are pointers if has no default
+ return !has_default;
+
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ return !has_default;
+ }
+ } else if (type->is_enum()) {
+ return !has_default;
+ } else if (type->is_struct() || type->is_xception()) {
+ return true;
+ } else if (type->is_map()) {
+ return has_default;
+ } else if (type->is_set()) {
+ return has_default;
+ } else if (type->is_list()) {
+ return has_default;
+ } else if (type->is_typedef()) {
+ return has_default;
+ }
+
+ throw "INVALID TYPE IN type_to_go_type: " + type->get_name();
+}
+
+std::string t_go_generator::camelcase(const std::string& value) const {
+ std::string value2(value);
+ std::setlocale(LC_ALL, "C"); // set locale to classic
+
+ // Fix common initialism in first word
+ fix_common_initialism(value2, 0);
+
+ // as long as we are changing things, let's change _ followed by lowercase to
+ // capital and fix common initialisms
+ for (std::string::size_type i = 1; i < value2.size() - 1; ++i) {
+ if (value2[i] == '_') {
+ if (islower(value2[i + 1])) {
+ value2.replace(i, 2, 1, toupper(value2[i + 1]));
+ }
+
+ if (i > static_cast<std::string::size_type>(std::numeric_limits<int>().max())) {
+ throw "integer overflow in t_go_generator::camelcase, value = " + value;
+ }
+ fix_common_initialism(value2, static_cast<int>(i));
+ }
+ }
+
+ return value2;
+}
+
+// Checks to see if the word starting at i in value contains a common initialism
+// and if so replaces it with the upper case version of the word.
+void t_go_generator::fix_common_initialism(std::string& value, int i) const {
+ if (!ignore_initialisms_) {
+ size_t wordLen = value.find('_', i);
+ if (wordLen != std::string::npos) {
+ wordLen -= i;
+ }
+ std::string word = value.substr(i, wordLen);
+ std::transform(word.begin(), word.end(), word.begin(), ::toupper);
+ if (commonInitialisms.find(word) != commonInitialisms.end()) {
+ value.replace(i, word.length(), word);
+ }
+ }
+}
+
+std::string t_go_generator::publicize(const std::string& value, bool is_args_or_result) const {
+ if (value.size() <= 0) {
+ return value;
+ }
+
+ std::string value2(value), prefix;
+
+ string::size_type dot_pos = value.rfind('.');
+ if (dot_pos != string::npos) {
+ prefix = value.substr(0, dot_pos + 1) + prefix;
+ value2 = value.substr(dot_pos + 1);
+ }
+
+ if (!isupper(value2[0])) {
+ value2[0] = toupper(value2[0]);
+ }
+
+ value2 = camelcase(value2);
+
+ // final length before further checks, the string may become longer
+ size_t len_before = value2.length();
+
+ // IDL identifiers may start with "New" which interferes with the CTOR pattern
+ // Adding an extra underscore to all those identifiers solves this
+ if ((len_before >= 3) && (value2.substr(0, 3) == "New")) {
+ value2 += '_';
+ }
+
+ // IDL identifiers may end with "Args"/"Result" which interferes with the implicit service
+ // function structs
+ // Adding another extra underscore to all those identifiers solves this
+ // Suppress this check for the actual helper struct names
+ if (!is_args_or_result) {
+ bool ends_with_args = (len_before >= 4) && (value2.substr(len_before - 4, 4) == "Args");
+ bool ends_with_rslt = (len_before >= 6) && (value2.substr(len_before - 6, 6) == "Result");
+ if (ends_with_args || ends_with_rslt) {
+ value2 += '_';
+ }
+ }
+
+ // Avoid naming collisions with other services
+ if (is_args_or_result) {
+ prefix += publicize(service_name_);
+ }
+
+ return prefix + value2;
+}
+
+std::string t_go_generator::new_prefix(const std::string& value) const {
+ if (value.size() <= 0) {
+ return value;
+ }
+
+ string::size_type dot_pos = value.rfind('.');
+ if (dot_pos != string::npos) {
+ return value.substr(0, dot_pos + 1) + "New" + publicize(value.substr(dot_pos + 1));
+ }
+ return "New" + publicize(value);
+}
+
+std::string t_go_generator::privatize(const std::string& value) const {
+ if (value.size() <= 0) {
+ return value;
+ }
+
+ std::string value2(value);
+
+ if (!islower(value2[0])) {
+ value2[0] = tolower(value2[0]);
+ }
+
+ value2 = camelcase(value2);
+
+ return value2;
+}
+
+std::string t_go_generator::variable_name_to_go_name(const std::string& value) {
+ if (value.size() <= 0) {
+ return value;
+ }
+
+ std::string value2(value);
+ std::transform(value2.begin(), value2.end(), value2.begin(), ::tolower);
+
+ switch (value[0]) {
+ case 'b':
+ case 'B':
+ if (value2 != "break") {
+ return value;
+ }
+
+ break;
+
+ case 'c':
+ case 'C':
+ if (value2 != "case" && value2 != "chan" && value2 != "const" && value2 != "continue") {
+ return value;
+ }
+
+ break;
+
+ case 'd':
+ case 'D':
+ if (value2 != "default" && value2 != "defer") {
+ return value;
+ }
+
+ break;
+
+ case 'e':
+ case 'E':
+ if (value2 != "else" && value2 != "error") {
+ return value;
+ }
+
+ break;
+
+ case 'f':
+ case 'F':
+ if (value2 != "fallthrough" && value2 != "for" && value2 != "func") {
+ return value;
+ }
+
+ break;
+
+ case 'g':
+ case 'G':
+ if (value2 != "go" && value2 != "goto") {
+ return value;
+ }
+
+ break;
+
+ case 'i':
+ case 'I':
+ if (value2 != "if" && value2 != "import" && value2 != "interface") {
+ return value;
+ }
+
+ break;
+
+ case 'm':
+ case 'M':
+ if (value2 != "map") {
+ return value;
+ }
+
+ break;
+
+ case 'p':
+ case 'P':
+ if (value2 != "package") {
+ return value;
+ }
+
+ break;
+
+ case 'r':
+ case 'R':
+ if (value2 != "range" && value2 != "return") {
+ return value;
+ }
+
+ break;
+
+ case 's':
+ case 'S':
+ if (value2 != "select" && value2 != "struct" && value2 != "switch") {
+ return value;
+ }
+
+ break;
+
+ case 't':
+ case 'T':
+ if (value2 != "type") {
+ return value;
+ }
+
+ break;
+
+ case 'v':
+ case 'V':
+ if (value2 != "var") {
+ return value;
+ }
+
+ break;
+
+ default:
+ return value;
+ }
+
+ return value2 + "_a1";
+}
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_go_generator::init_generator() {
+ // Make output directory
+ string module = get_real_go_module(program_);
+ string target = module;
+ package_dir_ = get_out_dir();
+
+ // This set is taken from https://github.com/golang/lint/blob/master/lint.go#L692
+ commonInitialisms.insert("API");
+ commonInitialisms.insert("ASCII");
+ commonInitialisms.insert("CPU");
+ commonInitialisms.insert("CSS");
+ commonInitialisms.insert("DNS");
+ commonInitialisms.insert("EOF");
+ commonInitialisms.insert("GUID");
+ commonInitialisms.insert("HTML");
+ commonInitialisms.insert("HTTP");
+ commonInitialisms.insert("HTTPS");
+ commonInitialisms.insert("ID");
+ commonInitialisms.insert("IP");
+ commonInitialisms.insert("JSON");
+ commonInitialisms.insert("LHS");
+ commonInitialisms.insert("QPS");
+ commonInitialisms.insert("RAM");
+ commonInitialisms.insert("RHS");
+ commonInitialisms.insert("RPC");
+ commonInitialisms.insert("SLA");
+ commonInitialisms.insert("SMTP");
+ commonInitialisms.insert("SSH");
+ commonInitialisms.insert("TCP");
+ commonInitialisms.insert("TLS");
+ commonInitialisms.insert("TTL");
+ commonInitialisms.insert("UDP");
+ commonInitialisms.insert("UI");
+ commonInitialisms.insert("UID");
+ commonInitialisms.insert("UUID");
+ commonInitialisms.insert("URI");
+ commonInitialisms.insert("URL");
+ commonInitialisms.insert("UTF8");
+ commonInitialisms.insert("VM");
+ commonInitialisms.insert("XML");
+ commonInitialisms.insert("XSRF");
+ commonInitialisms.insert("XSS");
+
+ // names of read and write methods
+ if (read_write_private_) {
+ read_method_name_ = "read";
+ write_method_name_ = "write";
+ } else {
+ read_method_name_ = "Read";
+ write_method_name_ = "Write";
+ }
+
+ while (true) {
+ // TODO: Do better error checking here.
+ MKDIR(package_dir_.c_str());
+
+ if (module.empty()) {
+ break;
+ }
+
+ string::size_type pos = module.find('.');
+
+ if (pos == string::npos) {
+ package_dir_ += "/";
+ package_dir_ += module;
+ package_name_ = module;
+ module.clear();
+ } else {
+ package_dir_ += "/";
+ package_dir_ += module.substr(0, pos);
+ module.erase(0, pos + 1);
+ }
+ }
+
+ string::size_type loc;
+
+ while ((loc = target.find(".")) != string::npos) {
+ target.replace(loc, 1, 1, '/');
+ }
+
+ // Make output files
+ f_types_name_ = package_dir_ + "/" + program_name_ + ".go";
+ f_types_.open(f_types_name_);
+
+ f_consts_name_ = package_dir_ + "/" + program_name_ + "-consts.go";
+ f_consts_.open(f_consts_name_);
+
+ // Print header
+ f_types_ << go_autogen_comment() << go_package() << render_includes(false);
+
+ f_consts_ << go_autogen_comment() << go_package() << render_includes(true);
+
+ f_const_values_ << endl << "func init() {" << endl;
+
+ // Create file for the GoUnusedProtection__ variable
+ string f_unused_prot_name_ = package_dir_ + "/" + "GoUnusedProtection__.go";
+ ofstream_with_content_based_conditional_update f_unused_prot_;
+ f_unused_prot_.open(f_unused_prot_name_.c_str());
+ f_unused_prot_ << go_autogen_comment() << go_package() << render_import_protection();
+ f_unused_prot_.close();
+}
+
+string t_go_generator::render_included_programs(string& unused_prot) {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ string local_namespace = program_->get_namespace("go");
+ for (auto include : includes) {
+ if (!local_namespace.empty() && local_namespace == include->get_namespace("go")) {
+ continue;
+ }
+
+ result += render_program_import(include, unused_prot);
+ }
+ return result;
+}
+
+string t_go_generator::render_program_import(const t_program* program, string& unused_protection) {
+ string result = "";
+
+ string go_module = get_real_go_module(program);
+ string go_path = go_module;
+ size_t found = 0;
+ for (size_t j = 0; j < go_module.size(); j++) {
+ // Import statement uses slashes ('/') in namespace
+ if (go_module[j] == '.') {
+ go_path[j] = '/';
+ found = j + 1;
+ }
+ }
+
+ auto it = package_identifiers_.find(go_module);
+ auto last_component = go_module.substr(found);
+ if (it == package_identifiers_.end()) {
+ auto value = last_component;
+ // This final path component has already been used, let's construct a more unique alias
+ if (package_identifiers_set_.find(value) != package_identifiers_set_.end()) {
+ // TODO: This would produce more readable code if it appended trailing go_module
+ // path components to generate a more readable name unique identifier (e.g. use
+ // packageacommon as the alias for packagea/common instead of common=). But just
+ // appending an integer is much simpler code
+ value = tmp(value);
+ }
+ package_identifiers_set_.insert(value);
+ it = package_identifiers_.emplace(go_module, std::move(value)).first;
+ }
+ auto const& package_identifier = it->second;
+ result += "\t";
+ // if the package_identifier is different than final path component we need an alias
+ if (last_component.compare(package_identifier) != 0) {
+ result += package_identifier + " ";
+ }
+ string s;
+
+ for (auto const& e : package_identifiers_set_)
+ {
+ s += e;
+ s += ',';
+ }
+
+ s.pop_back();
+
+ result += "\"" + gen_package_prefix_ + go_path + "\"\n";
+ unused_protection += "var _ = " + package_identifier + ".GoUnusedProtection__\n";
+ return result;
+}
+
+string t_go_generator::render_system_packages(std::vector<string>& system_packages) {
+ string result = "";
+
+ for (vector<string>::iterator iter = system_packages.begin(); iter != system_packages.end(); ++iter) {
+ string package = *iter;
+ result += "\t\""+ package +"\"\n";
+
+ // Reserve these package names in case the collide with imported Thrift packages
+ package_identifiers_set_.insert(package);
+ package_identifiers_.emplace(package, package);
+ }
+ return result;
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program.
+ * If consts include the additional imports.
+ */
+string t_go_generator::render_includes(bool consts) {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ string unused_prot = "";
+ result += go_imports_begin(consts);
+ result += render_included_programs(unused_prot);
+
+ if (includes.size() > 0) {
+ result += "\n";
+ }
+
+ return result + go_imports_end() + unused_prot;
+}
+
+string t_go_generator::render_import_protection() {
+ return string("var GoUnusedProtection__ int;\n\n");
+}
+
+/**
+ * Renders all the imports necessary to use the accelerated TBinaryProtocol
+ */
+string t_go_generator::render_fastbinary_includes() {
+ return "";
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_go_generator::go_autogen_comment() {
+ return
+ std::string() +
+ "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n\n";
+}
+
+/**
+ * Prints standard thrift package
+ */
+string t_go_generator::go_package() {
+ return string("package ") + package_name_ + "\n\n";
+}
+
+/**
+ * Render the beginning of the import statement.
+ * If consts include the additional imports.
+ */
+string t_go_generator::go_imports_begin(bool consts) {
+ std::vector<string> system_packages;
+ system_packages.push_back("bytes");
+ system_packages.push_back("context");
+ system_packages.push_back("reflect");
+ // If not writing constants, and there are enums, need extra imports.
+ if (!consts && get_program()->get_enums().size() > 0) {
+ system_packages.push_back("database/sql/driver");
+ system_packages.push_back("errors");
+ }
+ system_packages.push_back("fmt");
+ system_packages.push_back(gen_thrift_import_);
+ return "import(\n" + render_system_packages(system_packages);
+}
+
+/**
+ * End the import statement, include undscore-assignments
+ *
+ * These "_ =" prevent the go compiler complaining about unused imports.
+ * This will have to do in lieu of more intelligent import statement construction
+ */
+string t_go_generator::go_imports_end() {
+ return string(
+ ")\n\n"
+ "// (needed to ensure safety because of naive import list construction.)\n"
+ "var _ = thrift.ZERO\n"
+ "var _ = fmt.Printf\n"
+ "var _ = context.Background\n"
+ "var _ = reflect.DeepEqual\n"
+ "var _ = bytes.Equal\n\n");
+}
+
+/**
+ * Closes the type files
+ */
+void t_go_generator::close_generator() {
+ f_const_values_ << "}" << endl << endl;
+ f_consts_ << f_const_values_.str();
+
+ // Close types and constants files
+ f_consts_.close();
+ f_types_.close();
+ format_go_output(f_types_name_);
+ format_go_output(f_consts_name_);
+}
+
+/**
+ * Generates a typedef.
+ *
+ * @param ttypedef The type definition
+ */
+void t_go_generator::generate_typedef(t_typedef* ttypedef) {
+ generate_go_docstring(f_types_, ttypedef);
+ string new_type_name(publicize(ttypedef->get_symbolic()));
+ string base_type(type_to_go_type(ttypedef->get_type()));
+
+ if (base_type == new_type_name) {
+ return;
+ }
+
+ f_types_ << "type " << new_type_name << " " << base_type << endl << endl;
+ // Generate a convenience function that converts an instance of a type
+ // (which may be a constant) into a pointer to an instance of a type.
+ f_types_ << "func " << new_type_name << "Ptr(v " << new_type_name << ") *" << new_type_name
+ << " { return &v }" << endl << endl;
+}
+
+/**
+ * Generates code for an enumerated type. Done using a class to scope
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_go_generator::generate_enum(t_enum* tenum) {
+ std::ostringstream to_string_mapping, from_string_mapping;
+ std::string tenum_name(publicize(tenum->get_name()));
+ generate_go_docstring(f_types_, tenum);
+ f_types_ << "type " << tenum_name << " int64" << endl << "const (" << endl;
+
+ to_string_mapping << indent() << "func (p " << tenum_name << ") String() string {" << endl;
+ to_string_mapping << indent() << " switch p {" << endl;
+
+ from_string_mapping << indent() << "func " << tenum_name << "FromString(s string) (" << tenum_name
+ << ", error) {" << endl;
+ from_string_mapping << indent() << " switch s {" << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ int value = -1;
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ value = (*c_iter)->get_value();
+
+ string iter_std_name(escape_string((*c_iter)->get_name()));
+ string iter_name((*c_iter)->get_name());
+ f_types_ << indent() << " " << tenum_name << "_" << iter_name << ' ' << tenum_name << " = "
+ << value << endl;
+ // Dictionaries to/from string names of enums
+ to_string_mapping << indent() << " case " << tenum_name << "_" << iter_name << ": return \""
+ << iter_std_name << "\"" << endl;
+
+ if (iter_std_name != escape_string(iter_name)) {
+ from_string_mapping << indent() << " case \"" << iter_std_name << "\", \""
+ << escape_string(iter_name) << "\": return " << tenum_name << "_"
+ << iter_name << ", nil " << endl;
+ } else {
+ from_string_mapping << indent() << " case \"" << iter_std_name << "\": return " << tenum_name
+ << "_" << iter_name << ", nil " << endl;
+ }
+ }
+
+ to_string_mapping << indent() << " }" << endl;
+ to_string_mapping << indent() << " return \"<UNSET>\"" << endl;
+ to_string_mapping << indent() << "}" << endl;
+ from_string_mapping << indent() << " }" << endl;
+ from_string_mapping << indent() << " return " << tenum_name << "(0),"
+ << " fmt.Errorf(\"not a valid " << tenum_name << " string\")" << endl;
+ from_string_mapping << indent() << "}" << endl;
+
+ f_types_ << ")" << endl << endl << to_string_mapping.str() << endl << from_string_mapping.str()
+ << endl << endl;
+
+ // Generate a convenience function that converts an instance of an enum
+ // (which may be a constant) into a pointer to an instance of that enum
+ // type.
+ f_types_ << "func " << tenum_name << "Ptr(v " << tenum_name << ") *" << tenum_name
+ << " { return &v }" << endl << endl;
+
+ // Generate MarshalText
+ f_types_ << "func (p " << tenum_name << ") MarshalText() ([]byte, error) {" << endl;
+ f_types_ << "return []byte(p.String()), nil" << endl;
+ f_types_ << "}" << endl << endl;
+
+ // Generate UnmarshalText
+ f_types_ << "func (p *" << tenum_name << ") UnmarshalText(text []byte) error {" << endl;
+ f_types_ << "q, err := " << tenum_name << "FromString(string(text))" << endl;
+ f_types_ << "if (err != nil) {" << endl << "return err" << endl << "}" << endl;
+ f_types_ << "*p = q" << endl;
+ f_types_ << "return nil" << endl;
+ f_types_ << "}" << endl << endl;
+
+ // Generate Scan for sql.Scanner interface
+ f_types_ << "func (p *" << tenum_name << ") Scan(value interface{}) error {" <<endl;
+ f_types_ << "v, ok := value.(int64)" <<endl;
+ f_types_ << "if !ok {" <<endl;
+ f_types_ << "return errors.New(\"Scan value is not int64\")" <<endl;
+ f_types_ << "}" <<endl;
+ f_types_ << "*p = " << tenum_name << "(v)" << endl;
+ f_types_ << "return nil" << endl;
+ f_types_ << "}" << endl << endl;
+
+ // Generate Value for driver.Valuer interface
+ f_types_ << "func (p * " << tenum_name << ") Value() (driver.Value, error) {" <<endl;
+ f_types_ << " if p == nil {" << endl;
+ f_types_ << " return nil, nil" << endl;
+ f_types_ << " }" << endl;
+ f_types_ << "return int64(*p), nil" << endl;
+ f_types_ << "}" << endl;
+
+}
+
+/**
+ * Generate a constant value
+ */
+void t_go_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = publicize(tconst->get_name());
+ t_const_value* value = tconst->get_value();
+ if (type->is_base_type() || type->is_enum()) {
+ indent(f_consts_) << "const " << name << " = " << render_const_value(type, value, name) << endl;
+ } else {
+ f_const_values_ << indent() << name << " = " << render_const_value(type, value, name) << endl
+ << endl;
+
+ f_consts_ << indent() << "var " << name << " " << type_to_go_type(type) << endl;
+ }
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_go_generator::render_const_value(t_type* type, t_const_value* value, const string& name, bool opt) {
+ type = get_true_type(type);
+ std::ostringstream out;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ if (opt) {
+ out << "&(&struct{x ";
+ switch (tbase) {
+ case t_base_type::TYPE_BOOL:
+ out << "bool}{";
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+
+ case t_base_type::TYPE_I8:
+ out << "int8}{";
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I16:
+ out << "int16}{";
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ out << "int32}{";
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ out << "int64}{";
+ out << value->get_integer();
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ out << "float64}{";
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+
+ case t_base_type::TYPE_STRING:
+ out << "string}{";
+ out << '"' + get_escaped_string(value) + '"';
+ break;
+
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ out << "}).x";
+ } else {
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "[]byte(\"" << get_escaped_string(value) << "\")";
+ } else {
+ out << '"' << get_escaped_string(value) << '"';
+ }
+
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ if (opt) {
+ out << "&(&struct{x int}{";
+ }
+ out << value->get_integer();
+ if (opt) {
+ out << "}).x";
+ }
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+
+ break;
+
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ } else if (type->is_enum()) {
+ indent(out) << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << "&" << publicize(type_name(type)) << "{";
+ indent_up();
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ bool is_optional = false;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ is_optional = is_pointer_field(*f_iter);
+ }
+ }
+
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ out << endl << indent() << publicize(v_iter->first->get_string()) << ": "
+ << render_const_value(field_type, v_iter->second, name, is_optional) << "," << endl;
+ }
+
+ indent_down();
+ out << "}";
+
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ out << "map[" << type_to_go_type(ktype) << "]" << type_to_go_type(vtype) << "{" << endl;
+ indent_up();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << render_const_value(ktype, v_iter->first, name) << ": "
+ << render_const_value(vtype, v_iter->second, name) << "," << endl;
+ }
+
+ indent_down();
+ out << indent() << "}";
+ } else if (type->is_list()) {
+ t_type* etype = ((t_list*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ out << "[]" << type_to_go_type(etype) << "{" << endl;
+ indent_up();
+ vector<t_const_value*>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << render_const_value(etype, *v_iter, name) << ", ";
+ }
+
+ indent_down();
+ out << indent() << "}";
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ out << "[]" << type_to_go_key_type(etype) << "{" << endl;
+ indent_up();
+ vector<t_const_value*>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << render_const_value(etype, *v_iter, name) << ", ";
+ }
+
+ indent_down();
+ out << indent() << "}";
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+
+ return out.str();
+}
+
+/**
+ * Generates a go struct
+ */
+void t_go_generator::generate_struct(t_struct* tstruct) {
+ generate_go_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_go_generator::generate_xception(t_struct* txception) {
+ generate_go_struct(txception, true);
+}
+
+/**
+ * Generates a go struct
+ */
+void t_go_generator::generate_go_struct(t_struct* tstruct, bool is_exception) {
+ generate_go_struct_definition(f_types_, tstruct, is_exception);
+}
+
+void t_go_generator::get_publicized_name_and_def_value(t_field* tfield,
+ string* OUT_pub_name,
+ t_const_value** OUT_def_value) const {
+ const string base_field_name = tfield->get_name();
+ const string escaped_field_name = escape_string(base_field_name);
+ *OUT_pub_name = publicize(escaped_field_name);
+ *OUT_def_value = tfield->get_value();
+}
+
+void t_go_generator::generate_go_struct_initializer(ostream& out,
+ t_struct* tstruct,
+ bool is_args_or_result) {
+ out << publicize(type_name(tstruct), is_args_or_result) << "{";
+ const vector<t_field*>& members = tstruct->get_members();
+ for (auto member : members) {
+ bool pointer_field = is_pointer_field(member);
+ string publicized_name;
+ t_const_value* def_value;
+ get_publicized_name_and_def_value(member, &publicized_name, &def_value);
+ if (!pointer_field && def_value != NULL && !omit_initialization(member)) {
+ out << endl << indent() << publicized_name << ": "
+ << render_field_initial_value(member, member->get_name(), pointer_field) << ","
+ << endl;
+ }
+ }
+
+ out << "}" << endl;
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_go_generator::generate_go_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result,
+ bool is_args) {
+ const vector<t_field*>& members = tstruct->get_members();
+ const vector<t_field*>& sorted_members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ std::string tstruct_name(publicize(tstruct->get_name(), is_args || is_result));
+ generate_go_docstring(out, tstruct);
+ out << indent() << "type " << tstruct_name << " struct {" << endl;
+ /*
+ Here we generate the structure specification for the fastbinary codec.
+ These specifications have the following structure:
+ thrift_spec -> tuple of item_spec
+ item_spec -> nil | (tag, type_enum, name, spec_args, default)
+ tag -> integer
+ type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ...
+ name -> string_literal
+ default -> nil # Handled by __init__
+ spec_args -> nil # For simple types
+ | (type_enum, spec_args) # Value type for list/set
+ | (type_enum, spec_args, type_enum, spec_args)
+ # Key and value for map
+ | (class_name, spec_args_ptr) # For struct/exception
+ class_name -> identifier # Basically a pointer to the class
+ spec_args_ptr -> expression # just class_name.spec_args
+
+ TODO(dreiss): Consider making this work for structs with negative tags.
+ */
+ // TODO(dreiss): Look into generating an empty tuple instead of nil
+ // for structures with no members.
+ // TODO(dreiss): Test encoding of structs where some inner structs
+ // don't have thrift_spec.
+ indent_up();
+
+ int num_setable = 0;
+ if (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) {
+ int sorted_keys_pos = 0;
+
+ for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) {
+ // Set field to optional if field is union, this is so we can get a
+ // pointer to the field.
+ if (tstruct->is_union())
+ (*m_iter)->set_req(t_field::T_OPTIONAL);
+ if (sorted_keys_pos != (*m_iter)->get_key()) {
+ int first_unused = (std::max)(1, sorted_keys_pos++);
+ while (sorted_keys_pos != (*m_iter)->get_key()) {
+ ++sorted_keys_pos;
+ }
+ int last_unused = sorted_keys_pos - 1;
+ if (first_unused < last_unused) {
+ indent(out) << "// unused fields # " << first_unused << " to " << last_unused << endl;
+ } else if (first_unused == last_unused) {
+ indent(out) << "// unused field # " << first_unused << endl;
+ }
+ }
+
+ t_type* fieldType = (*m_iter)->get_type();
+ string goType = type_to_go_type_with_opt(fieldType, is_pointer_field(*m_iter));
+ string gotag = "db:\"" + escape_string((*m_iter)->get_name()) + "\" ";
+
+ // Only add the `omitempty` tag if this field is optional and has no default value.
+ // Otherwise a proper value like `false` for a bool field will be ommitted from
+ // the JSON output since Go Marshal won't output `zero values`.
+ bool has_default = (*m_iter)->get_value();
+ bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
+ if (is_optional && !has_default) {
+ gotag += "json:\"" + escape_string((*m_iter)->get_name()) + ",omitempty\"";
+ } else {
+ gotag += "json:\"" + escape_string((*m_iter)->get_name()) + "\"";
+ }
+
+ // Check for user override of db and json tags using "go.tag"
+ std::map<string, string>::iterator it = (*m_iter)->annotations_.find("go.tag");
+ if (it != (*m_iter)->annotations_.end()) {
+ gotag = it->second;
+ }
+ indent(out) << publicize((*m_iter)->get_name()) << " " << goType << " `thrift:\""
+ << escape_string((*m_iter)->get_name()) << "," << sorted_keys_pos;
+ if ((*m_iter)->get_req() == t_field::T_REQUIRED) {
+ out << ",required";
+ }
+
+ out << "\" " << gotag << "`" << endl;
+ sorted_keys_pos++;
+ }
+ } else {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ // This fills in default values, as opposed to nulls
+ out << indent() << publicize((*m_iter)->get_name()) << " "
+ << type_to_go_type((*m_iter)->get_type()) << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ out << indent() << "func New" << tstruct_name << "() *" << tstruct_name << " {" << endl;
+ out << indent() << " return &";
+ generate_go_struct_initializer(out, tstruct, is_result || is_args);
+ out << indent() << "}" << endl << endl;
+ // Default values for optional fields
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string publicized_name;
+ t_const_value* def_value;
+ get_publicized_name_and_def_value(*m_iter, &publicized_name, &def_value);
+ t_type* fieldType = (*m_iter)->get_type();
+ string goType = type_to_go_type_with_opt(fieldType, false);
+ string def_var_name = tstruct_name + "_" + publicized_name + "_DEFAULT";
+ if ((*m_iter)->get_req() == t_field::T_OPTIONAL || is_pointer_field(*m_iter)) {
+ out << indent() << "var " << def_var_name << " " << goType;
+ if (def_value != NULL) {
+ out << " = " << render_const_value(fieldType, def_value, (*m_iter)->get_name());
+ }
+ out << endl;
+ }
+
+ // num_setable is used for deciding if Count* methods will be generated for union fields.
+ // This applies to all nullable fields including slices (used for set, list and binary) and maps, not just pointers.
+ t_type* type = fieldType->get_true_type();
+ if (is_pointer_field(*m_iter)|| type->is_map() || type->is_set() || type->is_list() || type->is_binary()) {
+ num_setable += 1;
+ }
+
+ if (is_pointer_field(*m_iter)) {
+ string goOptType = type_to_go_type_with_opt(fieldType, true);
+ string maybepointer = goOptType != goType ? "*" : "";
+ out << indent() << "func (p *" << tstruct_name << ") Get" << publicized_name << "() "
+ << goType << " {" << endl;
+ out << indent() << " if !p.IsSet" << publicized_name << "() {" << endl;
+ out << indent() << " return " << def_var_name << endl;
+ out << indent() << " }" << endl;
+ out << indent() << "return " << maybepointer << "p." << publicized_name << endl;
+ out << indent() << "}" << endl;
+ } else {
+ out << endl;
+ out << indent() << "func (p *" << tstruct_name << ") Get" << publicized_name << "() "
+ << goType << " {" << endl;
+ out << indent() << " return p." << publicized_name << endl;
+ out << indent() << "}" << endl;
+ }
+ }
+
+ if (tstruct->is_union() && num_setable > 0) {
+ generate_countsetfields_helper(out, tstruct, tstruct_name, is_result);
+ }
+
+ generate_isset_helpers(out, tstruct, tstruct_name, is_result);
+ generate_go_struct_reader(out, tstruct, tstruct_name, is_result);
+ generate_go_struct_writer(out, tstruct, tstruct_name, is_result, num_setable > 0);
+
+ out << indent() << "func (p *" << tstruct_name << ") String() string {" << endl;
+ out << indent() << " if p == nil {" << endl;
+ out << indent() << " return \"<nil>\"" << endl;
+ out << indent() << " }" << endl;
+ out << indent() << " return fmt.Sprintf(\"" << escape_string(tstruct_name) << "(%+v)\", *p)"
+ << endl;
+ out << indent() << "}" << endl << endl;
+
+ if (is_exception) {
+ out << indent() << "func (p *" << tstruct_name << ") Error() string {" << endl;
+ out << indent() << " return p.String()" << endl;
+ out << indent() << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates the IsSet helper methods for a struct
+ */
+void t_go_generator::generate_isset_helpers(ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result) {
+ (void)is_result;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const string escaped_tstruct_name(escape_string(tstruct->get_name()));
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ const string field_name(publicize(escape_string((*f_iter)->get_name())));
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL || is_pointer_field(*f_iter)) {
+ out << indent() << "func (p *" << tstruct_name << ") IsSet" << field_name << "() bool {"
+ << endl;
+ indent_up();
+ t_type* ttype = (*f_iter)->get_type()->get_true_type();
+ bool is_byteslice = ttype->is_binary();
+ bool compare_to_nil_only = ttype->is_set() || ttype->is_list() || ttype->is_map()
+ || (is_byteslice && !(*f_iter)->get_value());
+ if (is_pointer_field(*f_iter) || compare_to_nil_only) {
+ out << indent() << "return p." << field_name << " != nil" << endl;
+ } else {
+ string def_var_name = tstruct_name + "_" + field_name + "_DEFAULT";
+ if (is_byteslice) {
+ out << indent() << "return !bytes.Equal(p." << field_name << ", " << def_var_name << ")"
+ << endl;
+ } else {
+ out << indent() << "return p." << field_name << " != " << def_var_name << endl;
+ }
+ }
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+ }
+}
+
+/**
+ * Generates the CountSetFields helper method for a struct
+ */
+void t_go_generator::generate_countsetfields_helper(ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result) {
+ (void)is_result;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const string escaped_tstruct_name(escape_string(tstruct->get_name()));
+
+ out << indent() << "func (p *" << tstruct_name << ") CountSetFields" << tstruct_name << "() int {"
+ << endl;
+ indent_up();
+ out << indent() << "count := 0" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED)
+ continue;
+
+ t_type* type = (*f_iter)->get_type()->get_true_type();
+
+ if (!(is_pointer_field(*f_iter) || type->is_map() || type->is_set() || type->is_list() || type->is_binary()))
+ continue;
+
+ const string field_name(publicize(escape_string((*f_iter)->get_name())));
+
+ out << indent() << "if (p.IsSet" << field_name << "()) {" << endl;
+ indent_up();
+ out << indent() << "count++" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ out << indent() << "return count" << endl << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates the read method for a struct
+ */
+void t_go_generator::generate_go_struct_reader(ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result) {
+ (void)is_result;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ string escaped_tstruct_name(escape_string(tstruct->get_name()));
+ out << indent() << "func (p *" << tstruct_name << ") " << read_method_name_ << "(iprot thrift.TProtocol) error {"
+ << endl;
+ indent_up();
+ out << indent() << "if _, err := iprot.ReadStructBegin(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)"
+ << endl;
+ out << indent() << "}" << endl << endl;
+
+ // Required variables does not have IsSet functions, so we need tmp vars to check them.
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ const string field_name(publicize(escape_string((*f_iter)->get_name())));
+ indent(out) << "var isset" << field_name << " bool = false;" << endl;
+ }
+ }
+ out << endl;
+
+ // Loop over reading in fields
+ indent(out) << "for {" << endl;
+ indent_up();
+ // Read beginning field marker
+ out << indent() << "_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()" << endl;
+ out << indent() << "if err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf("
+ "\"%T field %d read error: \", p, fieldId), err)" << endl;
+ out << indent() << "}" << endl;
+ // Check for field STOP marker and break
+ out << indent() << "if fieldTypeId == thrift.STOP { break; }" << endl;
+
+ string thriftFieldTypeId;
+ // Generate deserialization code for known cases
+ int32_t field_id = -1;
+
+ // Switch statement on the field we are reading, false if no fields present
+ bool have_switch = !fields.empty();
+ if (have_switch) {
+ indent(out) << "switch fieldId {" << endl;
+ }
+
+ // All the fields we know
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ field_id = (*f_iter)->get_key();
+
+ // if negative id, ensure we generate a valid method name
+ string field_method_prefix("ReadField");
+ int32_t field_method_suffix = field_id;
+
+ if (field_method_suffix < 0) {
+ field_method_prefix += "_";
+ field_method_suffix *= -1;
+ }
+
+ out << indent() << "case " << field_id << ":" << endl;
+ indent_up();
+ thriftFieldTypeId = type_to_enum((*f_iter)->get_type());
+
+ if (thriftFieldTypeId == "thrift.BINARY") {
+ thriftFieldTypeId = "thrift.STRING";
+ }
+
+ out << indent() << "if fieldTypeId == " << thriftFieldTypeId << " {" << endl;
+ out << indent() << " if err := p." << field_method_prefix << field_method_suffix << "(iprot); err != nil {"
+ << endl;
+ out << indent() << " return err" << endl;
+ out << indent() << " }" << endl;
+
+ // Mark required field as read
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ const string field_name(publicize(escape_string((*f_iter)->get_name())));
+ out << indent() << " isset" << field_name << " = true" << endl;
+ }
+
+ out << indent() << "} else {" << endl;
+ out << indent() << " if err := iprot.Skip(fieldTypeId); err != nil {" << endl;
+ out << indent() << " return err" << endl;
+ out << indent() << " }" << endl;
+ out << indent() << "}" << endl;
+
+
+ indent_down();
+ }
+
+ // Begin switch default case
+ if (have_switch) {
+ out << indent() << "default:" << endl;
+ indent_up();
+ }
+
+ // Skip unknown fields in either case
+ out << indent() << "if err := iprot.Skip(fieldTypeId); err != nil {" << endl;
+ out << indent() << " return err" << endl;
+ out << indent() << "}" << endl;
+
+ // End switch default case
+ if (have_switch) {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ // Read field end marker
+ out << indent() << "if err := iprot.ReadFieldEnd(); err != nil {" << endl;
+ out << indent() << " return err" << endl;
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "if err := iprot.ReadStructEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf("
+ "\"%T read struct end error: \", p), err)" << endl;
+ out << indent() << "}" << endl;
+
+ // Return error if any required fields are missing.
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ const string field_name(publicize(escape_string((*f_iter)->get_name())));
+ out << indent() << "if !isset" << field_name << "{" << endl;
+ out << indent() << " return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, "
+ "fmt.Errorf(\"Required field " << field_name << " is not set\"));" << endl;
+ out << indent() << "}" << endl;
+ }
+ }
+
+ out << indent() << "return nil" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ string field_type_name(publicize((*f_iter)->get_type()->get_name()));
+ string field_name(publicize((*f_iter)->get_name()));
+ string field_method_prefix("ReadField");
+ int32_t field_id = (*f_iter)->get_key();
+ int32_t field_method_suffix = field_id;
+
+ if (field_method_suffix < 0) {
+ field_method_prefix += "_";
+ field_method_suffix *= -1;
+ }
+
+ out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_method_suffix
+ << "(iprot thrift.TProtocol) error {" << endl;
+ indent_up();
+ generate_deserialize_field(out, *f_iter, false, "p.");
+ indent_down();
+ out << indent() << " return nil" << endl;
+ out << indent() << "}" << endl << endl;
+ }
+}
+
+void t_go_generator::generate_go_struct_writer(ostream& out,
+ t_struct* tstruct,
+ const string& tstruct_name,
+ bool is_result,
+ bool uses_countsetfields) {
+ (void)is_result;
+ string name(tstruct->get_name());
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+ indent(out) << "func (p *" << tstruct_name << ") " << write_method_name_ << "(oprot thrift.TProtocol) error {" << endl;
+ indent_up();
+ if (tstruct->is_union() && uses_countsetfields) {
+ std::string tstruct_name(publicize(tstruct->get_name()));
+ out << indent() << "if c := p.CountSetFields" << tstruct_name << "(); c != 1 {" << endl
+ << indent()
+ << " return fmt.Errorf(\"%T write union: exactly one field must be set (%d set).\", p, c)"
+ << endl << indent() << "}" << endl;
+ }
+ out << indent() << "if err := oprot.WriteStructBegin(\"" << name << "\"); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf("
+ "\"%T write struct begin error: \", p), err) }" << endl;
+
+ string field_name;
+ string escape_field_name;
+ // t_const_value* field_default_value;
+ t_field::e_req field_required;
+ int32_t field_id = -1;
+
+ out << indent() << "if p != nil {" << endl;
+ indent_up();
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ string field_method_prefix("writeField");
+ field_name = (*f_iter)->get_name();
+ escape_field_name = escape_string(field_name);
+ field_id = (*f_iter)->get_key();
+ int32_t field_method_suffix = field_id;
+
+ if (field_method_suffix < 0) {
+ field_method_prefix += "_";
+ field_method_suffix *= -1;
+ }
+
+ out << indent() << "if err := p." << field_method_prefix << field_method_suffix
+ << "(oprot); err != nil { return err }" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ // Write the struct map
+ out << indent() << "if err := oprot.WriteFieldStop(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"write field stop error: \", err) }" << endl;
+ out << indent() << "if err := oprot.WriteStructEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"write struct stop error: \", err) }" << endl;
+ out << indent() << "return nil" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ string field_method_prefix("writeField");
+ field_id = (*f_iter)->get_key();
+ field_name = (*f_iter)->get_name();
+ escape_field_name = escape_string(field_name);
+ // field_default_value = (*f_iter)->get_value();
+ field_required = (*f_iter)->get_req();
+ int32_t field_method_suffix = field_id;
+
+ if (field_method_suffix < 0) {
+ field_method_prefix += "_";
+ field_method_suffix *= -1;
+ }
+
+ out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_method_suffix
+ << "(oprot thrift.TProtocol) (err error) {" << endl;
+ indent_up();
+
+ if (field_required == t_field::T_OPTIONAL) {
+ out << indent() << "if p.IsSet" << publicize(field_name) << "() {" << endl;
+ indent_up();
+ }
+
+ out << indent() << "if err := oprot.WriteFieldBegin(\"" << escape_field_name << "\", "
+ << type_to_enum((*f_iter)->get_type()) << ", " << field_id << "); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T write field begin error "
+ << field_id << ":" << escape_field_name << ": \", p), err) }" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "p.");
+
+ // Write field closer
+ out << indent() << "if err := oprot.WriteFieldEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T write field end error "
+ << field_id << ":" << escape_field_name << ": \", p), err) }" << endl;
+
+ if (field_required == t_field::T_OPTIONAL) {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ indent_down();
+ out << indent() << " return err" << endl;
+ out << indent() << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_go_generator::generate_service(t_service* tservice) {
+ string test_suffix("_test");
+ string filename = lowercase(service_name_);
+ string f_service_name;
+
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+ generate_service_remote(tservice);
+ f_types_ << endl;
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_go_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ f_types_ << "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_go_struct_definition(f_types_, ts, false, false, true);
+ generate_go_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_go_generator::generate_go_function_helpers(t_function* tfunction) {
+ if (!tfunction->is_oneway()) {
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ success.set_req(t_field::T_OPTIONAL);
+
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* f = *f_iter;
+ f->set_req(t_field::T_OPTIONAL);
+ result.append(f);
+ }
+
+ generate_go_struct_definition(f_types_, &result, false, true);
+ }
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_go_generator::generate_service_interface(t_service* tservice) {
+ string extends = "";
+ string extends_if = "";
+ string serviceName(publicize(tservice->get_name()));
+ string interfaceName = serviceName;
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ size_t index = extends.rfind(".");
+
+ if (index != string::npos) {
+ extends_if = "\n" + indent() + " " + extends.substr(0, index + 1)
+ + publicize(extends.substr(index + 1)) + "\n";
+ } else {
+ extends_if = "\n" + indent() + publicize(extends) + "\n";
+ }
+ }
+
+ f_types_ << indent() << "type " << interfaceName << " interface {" << extends_if;
+ indent_up();
+ generate_go_docstring(f_types_, tservice);
+ vector<t_function*> functions = tservice->get_functions();
+
+ if (!functions.empty()) {
+ f_types_ << endl;
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_go_docstring(f_types_, (*f_iter));
+ f_types_ << indent() << function_signature_if(*f_iter, "", true) << endl;
+ }
+ }
+
+ indent_down();
+ f_types_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_go_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_field = "";
+ string extends_client = "";
+ string extends_client_new = "";
+ string serviceName(publicize(tservice->get_name()));
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ size_t index = extends.rfind(".");
+
+ if (index != string::npos) {
+ extends_client = extends.substr(0, index + 1) + publicize(extends.substr(index + 1))
+ + "Client";
+ extends_client_new = extends.substr(0, index + 1) + "New"
+ + publicize(extends.substr(index + 1)) + "Client";
+ } else {
+ extends_client = publicize(extends) + "Client";
+ extends_client_new = "New" + extends_client;
+ }
+ }
+
+ extends_field = extends_client.substr(extends_client.find(".") + 1);
+
+ generate_go_docstring(f_types_, tservice);
+ f_types_ << indent() << "type " << serviceName << "Client struct {" << endl;
+ indent_up();
+
+ if (!extends_client.empty()) {
+ f_types_ << indent() << "*" << extends_client << endl;
+ } else {
+ f_types_ << indent() << "c thrift.TClient" << endl;
+ }
+
+ indent_down();
+ f_types_ << indent() << "}" << endl << endl;
+
+ // Legacy constructor function
+ f_types_ << indent() << "func New" << serviceName
+ << "ClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *" << serviceName
+ << "Client {" << endl;
+ indent_up();
+ f_types_ << indent() << "return &" << serviceName << "Client";
+
+ if (!extends.empty()) {
+ f_types_ << "{" << extends_field << ": " << extends_client_new << "Factory(t, f)}";
+ } else {
+ indent_up();
+ f_types_ << "{" << endl;
+ f_types_ << indent() << "c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t))," << endl;
+ indent_down();
+ f_types_ << indent() << "}" << endl;
+ }
+
+ indent_down();
+ f_types_ << indent() << "}" << endl << endl;
+ // Legacy constructor function with custom input & output protocols
+ f_types_
+ << indent() << "func New" << serviceName
+ << "ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *"
+ << serviceName << "Client {" << endl;
+ indent_up();
+ f_types_ << indent() << "return &" << serviceName << "Client";
+
+ if (!extends.empty()) {
+ f_types_ << "{" << extends_field << ": " << extends_client_new << "Protocol(t, iprot, oprot)}"
+ << endl;
+ } else {
+ indent_up();
+ f_types_ << "{" << endl;
+ f_types_ << indent() << "c: thrift.NewTStandardClient(iprot, oprot)," << endl;
+ indent_down();
+ f_types_ << indent() << "}" << endl;
+ }
+
+ indent_down();
+ f_types_ << indent() << "}" << endl << endl;
+
+ // Constructor function
+ f_types_ << indent() << "func New" << serviceName
+ << "Client(c thrift.TClient) *" << serviceName << "Client {" << endl;
+ indent_up();
+ f_types_ << indent() << "return &" << serviceName << "Client{" << endl;
+
+ indent_up();
+ if (!extends.empty()) {
+ f_types_ << indent() << extends_field << ": " << extends_client_new << "(c)," << endl;
+ } else {
+ f_types_ << indent() << "c: c," << endl;
+ }
+ indent_down();
+ f_types_ << indent() << "}" << endl;
+
+ indent_down();
+ f_types_ << indent() << "}" << endl << endl;
+
+ if (extends.empty()) {
+ f_types_ << indent() << "func (p *" << serviceName << "Client) Client_() thrift.TClient {" << endl;
+ indent_up();
+ f_types_ << indent() << "return p.c" << endl;
+ indent_down();
+ f_types_ << indent() << "}" << endl;
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = publicize((*f_iter)->get_name());
+ // Open function
+ generate_go_docstring(f_types_, (*f_iter));
+ f_types_ << indent() << "func (p *" << serviceName << "Client) "
+ << function_signature_if(*f_iter, "", true) << " {" << endl;
+ indent_up();
+
+ std::string method = (*f_iter)->get_name();
+ std::string argsType = publicize(method + "_args", true);
+ std::string argsName = tmp("_args");
+ f_types_ << indent() << "var " << argsName << " " << argsType << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_types_ << indent() << argsName << "." << publicize((*fld_iter)->get_name())
+ << " = " << variable_name_to_go_name((*fld_iter)->get_name()) << endl;
+ }
+
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultName = tmp("_result");
+ std::string resultType = publicize(method + "_result", true);
+ f_types_ << indent() << "var " << resultName << " " << resultType << endl;
+ f_types_ << indent() << "if err = p.Client_().Call(ctx, \""
+ << method << "\", &" << argsName << ", &" << resultName << "); err != nil {" << endl;
+
+ indent_up();
+ f_types_ << indent() << "return" << endl;
+ indent_down();
+ f_types_ << indent() << "}" << endl;
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ if (!xceptions.empty()) {
+ f_types_ << indent() << "switch {" << endl;
+
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ const std::string pubname = publicize((*x_iter)->get_name());
+ const std::string field = resultName + "." + pubname;
+
+ f_types_ << indent() << "case " << field << "!= nil:" << endl;
+ indent_up();
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_types_ << indent() << "return r, " << field << endl;
+ } else {
+ f_types_ << indent() << "return "<< field << endl;
+ }
+
+ indent_down();
+ }
+
+ f_types_ << indent() << "}" << endl << endl;
+ }
+
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_types_ << indent() << "return " << resultName << ".GetSuccess(), nil" << endl;
+ } else {
+ f_types_ << indent() << "return nil" << endl;
+ }
+ } else {
+ // TODO: would be nice to not to duplicate the call generation
+ f_types_ << indent() << "if err := p.Client_().Call(ctx, \""
+ << method << "\", &"<< argsName << ", nil); err != nil {" << endl;
+
+ indent_up();
+ f_types_ << indent() << "return err" << endl;
+ indent_down();
+ f_types_ << indent() << "}" << endl;
+ f_types_ << indent() << "return nil" << endl;
+ }
+
+ indent_down();
+ f_types_ << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a command line tool for making remote requests
+ *
+ * @param tservice The service to generate a remote for.
+ */
+void t_go_generator::generate_service_remote(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ t_service* parent = tservice->get_extends();
+
+ // collect inherited functions
+ while (parent != NULL) {
+ vector<t_function*> p_functions = parent->get_functions();
+ functions.insert(functions.end(), p_functions.begin(), p_functions.end());
+ parent = parent->get_extends();
+ }
+
+ // This file is not useful if there are no functions; don't generate it
+ if (functions.size() == 0) {
+ return;
+ }
+
+ string f_remote_dir = package_dir_ + "/" + underscore(service_name_) + "-remote";
+ MKDIR(f_remote_dir.c_str());
+
+ vector<t_function*>::iterator f_iter;
+ string f_remote_name = f_remote_dir + "/"
+ + underscore(service_name_) + "-remote.go";
+ ofstream_with_content_based_conditional_update f_remote;
+ f_remote.open(f_remote_name.c_str());
+
+ string unused_protection;
+
+ std::vector<string> system_packages;
+ system_packages.push_back("context");
+ system_packages.push_back("flag");
+ system_packages.push_back("fmt");
+ system_packages.push_back("math");
+ system_packages.push_back("net");
+ system_packages.push_back("net/url");
+ system_packages.push_back("os");
+ system_packages.push_back("strconv");
+ system_packages.push_back("strings");
+
+ f_remote << go_autogen_comment();
+ f_remote << indent() << "package main" << endl << endl;
+ f_remote << indent() << "import (" << endl;
+ f_remote << render_system_packages(system_packages);
+ f_remote << indent() << "\t\"" + gen_thrift_import_ + "\"" << endl;
+ f_remote << indent() << render_included_programs(unused_protection);
+ f_remote << render_program_import(program_, unused_protection);
+ f_remote << indent() << ")" << endl;
+ f_remote << indent() << endl;
+ f_remote << indent() << unused_protection; // filled in render_included_programs()
+ f_remote << indent() << endl;
+ f_remote << indent() << "func Usage() {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Usage of \", os.Args[0], \" "
+ "[-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:\")"
+ << endl;
+ f_remote << indent() << " flag.PrintDefaults()" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"\\nFunctions:\")" << endl;
+
+ string package_name_aliased = package_identifiers_[get_real_go_module(program_)];
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_remote << " fmt.Fprintln(os.Stderr, \" " << (*f_iter)->get_returntype()->get_name() << " "
+ << (*f_iter)->get_name() << "(";
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const std::vector<t_field*>& args = arg_struct->get_members();
+ std::vector<t_field*>::size_type num_args = args.size();
+ bool first = true;
+
+ for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ f_remote << ", ";
+ }
+
+ f_remote << args[i]->get_type()->get_name() << " " << args[i]->get_name();
+ }
+
+ f_remote << ")\")" << endl;
+ }
+
+ f_remote << indent() << " fmt.Fprintln(os.Stderr)" << endl;
+ f_remote << indent() << " os.Exit(0)" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << endl;
+
+ f_remote << indent() << "type httpHeaders map[string]string" << endl;
+ f_remote << indent() << endl;
+ f_remote << indent() << "func (h httpHeaders) String() string {" << endl;
+ f_remote << indent() << " var m map[string]string = h" << endl;
+ f_remote << indent() << " return fmt.Sprintf(\"%s\", m)" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << endl;
+ f_remote << indent() << "func (h httpHeaders) Set(value string) error {" << endl;
+ f_remote << indent() << " parts := strings.Split(value, \": \")" << endl;
+ f_remote << indent() << " if len(parts) != 2 {" << endl;
+ f_remote << indent() << " return fmt.Errorf(\"header should be of format 'Key: Value'\")" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << " h[parts[0]] = parts[1]" << endl;
+ f_remote << indent() << " return nil" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << endl;
+
+ f_remote << indent() << "func main() {" << endl;
+ indent_up();
+ f_remote << indent() << "flag.Usage = Usage" << endl;
+ f_remote << indent() << "var host string" << endl;
+ f_remote << indent() << "var port int" << endl;
+ f_remote << indent() << "var protocol string" << endl;
+ f_remote << indent() << "var urlString string" << endl;
+ f_remote << indent() << "var framed bool" << endl;
+ f_remote << indent() << "var useHttp bool" << endl;
+ f_remote << indent() << "headers := make(httpHeaders)" << endl;
+ f_remote << indent() << "var parsedUrl *url.URL" << endl;
+ f_remote << indent() << "var trans thrift.TTransport" << endl;
+ f_remote << indent() << "_ = strconv.Atoi" << endl;
+ f_remote << indent() << "_ = math.Abs" << endl;
+ f_remote << indent() << "flag.Usage = Usage" << endl;
+ f_remote << indent() << "flag.StringVar(&host, \"h\", \"localhost\", \"Specify host and port\")"
+ << endl;
+ f_remote << indent() << "flag.IntVar(&port, \"p\", 9090, \"Specify port\")" << endl;
+ f_remote << indent() << "flag.StringVar(&protocol, \"P\", \"binary\", \""
+ "Specify the protocol (binary, compact, simplejson, json)\")" << endl;
+ f_remote << indent() << "flag.StringVar(&urlString, \"u\", \"\", \"Specify the url\")" << endl;
+ f_remote << indent() << "flag.BoolVar(&framed, \"framed\", false, \"Use framed transport\")"
+ << endl;
+ f_remote << indent() << "flag.BoolVar(&useHttp, \"http\", false, \"Use http\")" << endl;
+ f_remote << indent() << "flag.Var(headers, \"H\", \"Headers to set on the http(s) request (e.g. -H \\\"Key: Value\\\")\")" << endl;
+ f_remote << indent() << "flag.Parse()" << endl;
+ f_remote << indent() << endl;
+ f_remote << indent() << "if len(urlString) > 0 {" << endl;
+ f_remote << indent() << " var err error" << endl;
+ f_remote << indent() << " parsedUrl, err = url.Parse(urlString)" << endl;
+ f_remote << indent() << " if err != nil {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << endl;
+ f_remote << indent() << " flag.Usage()" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << " host = parsedUrl.Host" << endl;
+ f_remote << indent() << " useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == \"http\" || parsedUrl.Scheme == \"https\""
+ << endl;
+ f_remote << indent() << "} else if useHttp {" << endl;
+ f_remote << indent() << " _, err := url.Parse(fmt.Sprint(\"http://\", host, \":\", port))"
+ << endl;
+ f_remote << indent() << " if err != nil {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << endl;
+ f_remote << indent() << " flag.Usage()" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << endl;
+ f_remote << indent() << "cmd := flag.Arg(0)" << endl;
+ f_remote << indent() << "var err error" << endl;
+ f_remote << indent() << "if useHttp {" << endl;
+ f_remote << indent() << " trans, err = thrift.NewTHttpClient(parsedUrl.String())" << endl;
+ f_remote << indent() << " if len(headers) > 0 {" << endl;
+ f_remote << indent() << " httptrans := trans.(*thrift.THttpClient)" << endl;
+ f_remote << indent() << " for key, value := range headers {" << endl;
+ f_remote << indent() << " httptrans.SetHeader(key, value)" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << "} else {" << endl;
+ f_remote << indent() << " portStr := fmt.Sprint(port)" << endl;
+ f_remote << indent() << " if strings.Contains(host, \":\") {" << endl;
+ f_remote << indent() << " host, portStr, err = net.SplitHostPort(host)" << endl;
+ f_remote << indent() << " if err != nil {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"error with host:\", err)"
+ << endl;
+ f_remote << indent() << " os.Exit(1)" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << " trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr))"
+ << endl;
+ f_remote << indent() << " if err != nil {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"error resolving address:\", err)" << endl;
+ f_remote << indent() << " os.Exit(1)" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << " if framed {" << endl;
+ f_remote << indent() << " trans = thrift.NewTFramedTransport(trans)" << endl;
+ f_remote << indent() << " }" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "if err != nil {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error creating transport\", err)" << endl;
+ f_remote << indent() << " os.Exit(1)" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "defer trans.Close()" << endl;
+ f_remote << indent() << "var protocolFactory thrift.TProtocolFactory" << endl;
+ f_remote << indent() << "switch protocol {" << endl;
+ f_remote << indent() << "case \"compact\":" << endl;
+ f_remote << indent() << " protocolFactory = thrift.NewTCompactProtocolFactory()" << endl;
+ f_remote << indent() << " break" << endl;
+ f_remote << indent() << "case \"simplejson\":" << endl;
+ f_remote << indent() << " protocolFactory = thrift.NewTSimpleJSONProtocolFactory()" << endl;
+ f_remote << indent() << " break" << endl;
+ f_remote << indent() << "case \"json\":" << endl;
+ f_remote << indent() << " protocolFactory = thrift.NewTJSONProtocolFactory()" << endl;
+ f_remote << indent() << " break" << endl;
+ f_remote << indent() << "case \"binary\", \"\":" << endl;
+ f_remote << indent() << " protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()" << endl;
+ f_remote << indent() << " break" << endl;
+ f_remote << indent() << "default:" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Invalid protocol specified: \", protocol)"
+ << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " os.Exit(1)" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "iprot := protocolFactory.GetProtocol(trans)" << endl;
+ f_remote << indent() << "oprot := protocolFactory.GetProtocol(trans)" << endl;
+ f_remote << indent() << "client := " << package_name_aliased << ".New" << publicize(service_name_)
+ << "Client(thrift.NewTStandardClient(iprot, oprot))" << endl;
+ f_remote << indent() << "if err := trans.Open(); err != nil {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error opening socket to \", "
+ "host, \":\", port, \" \", err)" << endl;
+ f_remote << indent() << " os.Exit(1)" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << endl;
+ f_remote << indent() << "switch cmd {" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const std::vector<t_field*>& args = arg_struct->get_members();
+ std::vector<t_field*>::size_type num_args = args.size();
+ string funcName((*f_iter)->get_name());
+ string pubName(publicize(funcName));
+ string argumentsName(publicize(funcName + "_args", true));
+ f_remote << indent() << "case \"" << escape_string(funcName) << "\":" << endl;
+ indent_up();
+ f_remote << indent() << "if flag.NArg() - 1 != " << num_args << " {" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"" << escape_string(pubName) << " requires "
+ << num_args << " args\")" << endl;
+ f_remote << indent() << " flag.Usage()" << endl;
+ f_remote << indent() << "}" << endl;
+
+ for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
+ std::vector<t_field*>::size_type flagArg = i + 1;
+ t_type* the_type(args[i]->get_type());
+ t_type* the_type2(get_true_type(the_type));
+
+ if (the_type2->is_enum()) {
+ f_remote << indent() << "tmp" << i << ", err := (strconv.Atoi(flag.Arg(" << flagArg << ")))"
+ << endl;
+ f_remote << indent() << "if err != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "argvalue" << i << " := " << package_name_aliased << "."
+ << publicize(the_type->get_name()) << "(tmp" << i << ")" << endl;
+ } else if (the_type2->is_base_type()) {
+ t_base_type::t_base e = ((t_base_type*)the_type2)->get_base();
+ string err(tmp("err"));
+
+ switch (e) {
+ case t_base_type::TYPE_VOID:
+ break;
+
+ case t_base_type::TYPE_STRING:
+ if (the_type2->is_binary()) {
+ f_remote << indent() << "argvalue" << i << " := []byte(flag.Arg(" << flagArg << "))"
+ << endl;
+ } else {
+ f_remote << indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ")" << endl;
+ }
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ f_remote << indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ") == \"true\""
+ << endl;
+ break;
+
+ case t_base_type::TYPE_I8:
+ f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg("
+ << flagArg << ")))" << endl;
+ f_remote << indent() << "if " << err << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "argvalue" << i << " := int8(tmp" << i << ")" << endl;
+ break;
+
+ case t_base_type::TYPE_I16:
+ f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg("
+ << flagArg << ")))" << endl;
+ f_remote << indent() << "if " << err << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "argvalue" << i << " := int16(tmp" << i << ")" << endl;
+ break;
+
+ case t_base_type::TYPE_I32:
+ f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg("
+ << flagArg << ")))" << endl;
+ f_remote << indent() << "if " << err << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "argvalue" << i << " := int32(tmp" << i << ")" << endl;
+ break;
+
+ case t_base_type::TYPE_I64:
+ f_remote << indent() << "argvalue" << i << ", " << err
+ << " := (strconv.ParseInt(flag.Arg(" << flagArg << "), 10, 64))" << endl;
+ f_remote << indent() << "if " << err << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ f_remote << indent() << "argvalue" << i << ", " << err
+ << " := (strconv.ParseFloat(flag.Arg(" << flagArg << "), 64))" << endl;
+ f_remote << indent() << "if " << err << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ break;
+
+ default:
+ throw("Invalid base type in generate_service_remote");
+ }
+
+ // f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg <<
+ // ")))";
+ } else if (the_type2->is_struct()) {
+ string arg(tmp("arg"));
+ string mbTrans(tmp("mbTrans"));
+ string err1(tmp("err"));
+ string factory(tmp("factory"));
+ string jsProt(tmp("jsProt"));
+ string err2(tmp("err"));
+ std::string tstruct_name(publicize(the_type->get_name()));
+ std::string tstruct_module( module_name(the_type));
+ if(tstruct_module.empty()) {
+ tstruct_module = package_name_aliased;
+ }
+
+ f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << endl;
+ f_remote << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))"
+ << endl;
+ f_remote << indent() << "defer " << mbTrans << ".Close()" << endl;
+ f_remote << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")"
+ << endl;
+ f_remote << indent() << "if " << err1 << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << factory << " := thrift.NewTJSONProtocolFactory()" << endl;
+ f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")"
+ << endl;
+ f_remote << indent() << "argvalue" << i << " := " << tstruct_module << ".New" << tstruct_name
+ << "()" << endl;
+ f_remote << indent() << err2 << " := argvalue" << i << "." << read_method_name_ << "(" << jsProt << ")" << endl;
+ f_remote << indent() << "if " << err2 << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ } else if (the_type2->is_container() || the_type2->is_xception()) {
+ string arg(tmp("arg"));
+ string mbTrans(tmp("mbTrans"));
+ string err1(tmp("err"));
+ string factory(tmp("factory"));
+ string jsProt(tmp("jsProt"));
+ string err2(tmp("err"));
+ std::string argName(publicize(args[i]->get_name()));
+ f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << endl;
+ f_remote << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))"
+ << endl;
+ f_remote << indent() << "defer " << mbTrans << ".Close()" << endl;
+ f_remote << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")"
+ << endl;
+ f_remote << indent() << "if " << err1 << " != nil { " << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << factory << " := thrift.NewTJSONProtocolFactory()" << endl;
+ f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")"
+ << endl;
+ f_remote << indent() << "containerStruct" << i << " := " << package_name_aliased << ".New"
+ << argumentsName << "()" << endl;
+ f_remote << indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "("
+ << jsProt << ")" << endl;
+ f_remote << indent() << "if " << err2 << " != nil {" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " return" << endl;
+ f_remote << indent() << "}" << endl;
+ f_remote << indent() << "argvalue" << i << " := containerStruct" << i << "." << argName
+ << endl;
+ } else {
+ throw("Invalid argument type in generate_service_remote");
+ }
+
+ if (the_type->is_typedef()) {
+ std::string typedef_module(module_name(the_type));
+ if(typedef_module.empty()) {
+ typedef_module = package_name_aliased;
+ }
+ f_remote << indent() << "value" << i << " := " << typedef_module << "."
+ << publicize(the_type->get_name()) << "(argvalue" << i << ")" << endl;
+ } else {
+ f_remote << indent() << "value" << i << " := argvalue" << i << endl;
+ }
+ }
+
+ f_remote << indent() << "fmt.Print(client." << pubName << "(";
+ bool argFirst = true;
+
+ f_remote << "context.Background()";
+ for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
+ if (argFirst) {
+ argFirst = false;
+ f_remote << ", ";
+ } else {
+ f_remote << ", ";
+ }
+
+ if (args[i]->get_type()->is_enum()) {
+ f_remote << "value" << i;
+ } else if (args[i]->get_type()->is_base_type()) {
+ t_base_type::t_base e = ((t_base_type*)(args[i]->get_type()))->get_base();
+
+ switch (e) {
+ case t_base_type::TYPE_VOID:
+ break;
+
+ case t_base_type::TYPE_STRING:
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ f_remote << "value" << i;
+ break;
+
+ default:
+ throw("Invalid base type in generate_service_remote");
+ }
+
+ // f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg <<
+ // ")))";
+ } else {
+ f_remote << "value" << i;
+ }
+ }
+
+ f_remote << "))" << endl;
+ f_remote << indent() << "fmt.Print(\"\\n\")" << endl;
+ f_remote << indent() << "break" << endl;
+ indent_down();
+ }
+
+ f_remote << indent() << "case \"\":" << endl;
+ f_remote << indent() << " Usage()" << endl;
+ f_remote << indent() << " break" << endl;
+ f_remote << indent() << "default:" << endl;
+ f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Invalid function \", cmd)" << endl;
+ f_remote << indent() << "}" << endl;
+ indent_down();
+ f_remote << indent() << "}" << endl;
+ // Close service file
+ f_remote.close();
+ format_go_output(f_remote_name);
+#ifndef _MSC_VER
+ // Make file executable, love that bitwise OR action
+ chmod(f_remote_name.c_str(),
+ S_IRUSR | S_IWUSR | S_IXUSR
+#ifndef _WIN32
+ | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+#endif
+ );
+#endif
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_go_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ string extends = "";
+ string extends_processor = "";
+ string extends_processor_new = "";
+ string serviceName(publicize(tservice->get_name()));
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ size_t index = extends.rfind(".");
+
+ if (index != string::npos) {
+ extends_processor = extends.substr(0, index + 1) + publicize(extends.substr(index + 1))
+ + "Processor";
+ extends_processor_new = extends.substr(0, index + 1) + "New"
+ + publicize(extends.substr(index + 1)) + "Processor";
+ } else {
+ extends_processor = publicize(extends) + "Processor";
+ extends_processor_new = "New" + extends_processor;
+ }
+ }
+
+ string pServiceName(privatize(tservice->get_name()));
+ // Generate the header portion
+ string self(tmp("self"));
+
+ if (extends_processor.empty()) {
+ f_types_ << indent() << "type " << serviceName << "Processor struct {" << endl;
+ f_types_ << indent() << " processorMap map[string]thrift.TProcessorFunction" << endl;
+ f_types_ << indent() << " handler " << serviceName << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func (p *" << serviceName
+ << "Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {"
+ << endl;
+ f_types_ << indent() << " p.processorMap[key] = processor" << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func (p *" << serviceName
+ << "Processor) GetProcessorFunction(key string) "
+ "(processor thrift.TProcessorFunction, ok bool) {" << endl;
+ f_types_ << indent() << " processor, ok = p.processorMap[key]" << endl;
+ f_types_ << indent() << " return processor, ok" << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func (p *" << serviceName
+ << "Processor) ProcessorMap() map[string]thrift.TProcessorFunction {" << endl;
+ f_types_ << indent() << " return p.processorMap" << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func New" << serviceName << "Processor(handler " << serviceName
+ << ") *" << serviceName << "Processor {" << endl << endl;
+ f_types_
+ << indent() << " " << self << " := &" << serviceName
+ << "Processor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)}"
+ << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string escapedFuncName(escape_string((*f_iter)->get_name()));
+ f_types_ << indent() << " " << self << ".processorMap[\"" << escapedFuncName << "\"] = &"
+ << pServiceName << "Processor" << publicize((*f_iter)->get_name())
+ << "{handler:handler}" << endl;
+ }
+
+ string x(tmp("x"));
+ f_types_ << indent() << "return " << self << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func (p *" << serviceName
+ << "Processor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err "
+ "thrift.TException) {" << endl;
+ f_types_ << indent() << " name, _, seqId, err := iprot.ReadMessageBegin()" << endl;
+ f_types_ << indent() << " if err != nil { return false, err }" << endl;
+ f_types_ << indent() << " if processor, ok := p.GetProcessorFunction(name); ok {" << endl;
+ f_types_ << indent() << " return processor.Process(ctx, seqId, iprot, oprot)" << endl;
+ f_types_ << indent() << " }" << endl;
+ f_types_ << indent() << " iprot.Skip(thrift.STRUCT)" << endl;
+ f_types_ << indent() << " iprot.ReadMessageEnd()" << endl;
+ f_types_ << indent() << " " << x
+ << " := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function "
+ "\" + name)" << endl;
+ f_types_ << indent() << " oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)" << endl;
+ f_types_ << indent() << " " << x << ".Write(oprot)" << endl;
+ f_types_ << indent() << " oprot.WriteMessageEnd()" << endl;
+ f_types_ << indent() << " oprot.Flush(ctx)" << endl;
+ f_types_ << indent() << " return false, " << x << endl;
+ f_types_ << indent() << "" << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ } else {
+ f_types_ << indent() << "type " << serviceName << "Processor struct {" << endl;
+ f_types_ << indent() << " *" << extends_processor << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func New" << serviceName << "Processor(handler " << serviceName
+ << ") *" << serviceName << "Processor {" << endl;
+ f_types_ << indent() << " " << self << " := &" << serviceName << "Processor{"
+ << extends_processor_new << "(handler)}" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string escapedFuncName(escape_string((*f_iter)->get_name()));
+ f_types_ << indent() << " " << self << ".AddToProcessorMap(\"" << escapedFuncName
+ << "\", &" << pServiceName << "Processor" << publicize((*f_iter)->get_name())
+ << "{handler:handler})" << endl;
+ }
+
+ f_types_ << indent() << " return " << self << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ }
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ f_types_ << endl;
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_go_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ // Open function
+ string processorName = privatize(tservice->get_name()) + "Processor"
+ + publicize(tfunction->get_name());
+ string argsname = publicize(tfunction->get_name() + "_args", true);
+ string resultname = publicize(tfunction->get_name() + "_result", true);
+
+ // t_struct* xs = tfunction->get_xceptions();
+ // const std::vector<t_field*>& xceptions = xs->get_members();
+ f_types_ << indent() << "type " << processorName << " struct {" << endl;
+ f_types_ << indent() << " handler " << publicize(tservice->get_name()) << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "func (p *" << processorName
+ << ") Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err "
+ "thrift.TException) {" << endl;
+ indent_up();
+ f_types_ << indent() << "args := " << argsname << "{}" << endl;
+ f_types_ << indent() << "if err = args." << read_method_name_ << "(iprot); err != nil {" << endl;
+ f_types_ << indent() << " iprot.ReadMessageEnd()" << endl;
+ if (!tfunction->is_oneway()) {
+ f_types_ << indent()
+ << " x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())"
+ << endl;
+ f_types_ << indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name())
+ << "\", thrift.EXCEPTION, seqId)" << endl;
+ f_types_ << indent() << " x.Write(oprot)" << endl;
+ f_types_ << indent() << " oprot.WriteMessageEnd()" << endl;
+ f_types_ << indent() << " oprot.Flush(ctx)" << endl;
+ }
+ f_types_ << indent() << " return false, err" << endl;
+ f_types_ << indent() << "}" << endl << endl;
+ f_types_ << indent() << "iprot.ReadMessageEnd()" << endl;
+
+ if (!tfunction->is_oneway()) {
+ f_types_ << indent() << "result := " << resultname << "{}" << endl;
+ }
+ bool need_reference = type_need_reference(tfunction->get_returntype());
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_types_ << "var retval " << type_to_go_type(tfunction->get_returntype()) << endl;
+ }
+
+ f_types_ << indent() << "var err2 error" << endl;
+ f_types_ << indent() << "if ";
+
+ if (!tfunction->is_oneway()) {
+ if (!tfunction->get_returntype()->is_void()) {
+ f_types_ << "retval, ";
+ }
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ f_types_ << "err2 = p.handler." << publicize(tfunction->get_name()) << "(";
+ bool first = true;
+
+ f_types_ << "ctx";
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ f_types_ << ", ";
+ } else {
+ f_types_ << ", ";
+ }
+
+ f_types_ << "args." << publicize((*f_iter)->get_name());
+ }
+
+ f_types_ << "); err2 != nil {" << endl;
+
+ t_struct* exceptions = tfunction->get_xceptions();
+ const vector<t_field*>& x_fields = exceptions->get_members();
+ if (!x_fields.empty()) {
+ f_types_ << indent() << "switch v := err2.(type) {" << endl;
+
+ vector<t_field*>::const_iterator xf_iter;
+
+ for (xf_iter = x_fields.begin(); xf_iter != x_fields.end(); ++xf_iter) {
+ f_types_ << indent() << " case " << type_to_go_type(((*xf_iter)->get_type())) << ":"
+ << endl;
+ f_types_ << indent() << "result." << publicize((*xf_iter)->get_name()) << " = v" << endl;
+ }
+
+ f_types_ << indent() << " default:" << endl;
+ }
+
+ if (!tfunction->is_oneway()) {
+ f_types_ << indent() << " x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "
+ "\"Internal error processing " << escape_string(tfunction->get_name())
+ << ": \" + err2.Error())" << endl;
+ f_types_ << indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name())
+ << "\", thrift.EXCEPTION, seqId)" << endl;
+ f_types_ << indent() << " x.Write(oprot)" << endl;
+ f_types_ << indent() << " oprot.WriteMessageEnd()" << endl;
+ f_types_ << indent() << " oprot.Flush(ctx)" << endl;
+ }
+
+ f_types_ << indent() << " return true, err2" << endl;
+
+ if (!x_fields.empty()) {
+ f_types_ << indent() << "}" << endl;
+ }
+
+ f_types_ << indent() << "}"; // closes err2 != nil
+
+ if (!tfunction->is_oneway()) {
+ if (!tfunction->get_returntype()->is_void()) {
+ f_types_ << " else {" << endl; // make sure we set Success retval only on success
+ indent_up();
+ f_types_ << indent() << "result.Success = ";
+ if (need_reference) {
+ f_types_ << "&";
+ }
+ f_types_ << "retval" << endl;
+ indent_down();
+ f_types_ << "}" << endl;
+ } else {
+ f_types_ << endl;
+ }
+ f_types_ << indent() << "if err2 = oprot.WriteMessageBegin(\""
+ << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqId); err2 != nil {"
+ << endl;
+ f_types_ << indent() << " err = err2" << endl;
+ f_types_ << indent() << "}" << endl;
+ f_types_ << indent() << "if err2 = result." << write_method_name_ << "(oprot); err == nil && err2 != nil {" << endl;
+ f_types_ << indent() << " err = err2" << endl;
+ f_types_ << indent() << "}" << endl;
+ f_types_ << indent() << "if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {"
+ << endl;
+ f_types_ << indent() << " err = err2" << endl;
+ f_types_ << indent() << "}" << endl;
+ f_types_ << indent() << "if err2 = oprot.Flush(ctx); err == nil && err2 != nil {" << endl;
+ f_types_ << indent() << " err = err2" << endl;
+ f_types_ << indent() << "}" << endl;
+ f_types_ << indent() << "if err != nil {" << endl;
+ f_types_ << indent() << " return" << endl;
+ f_types_ << indent() << "}" << endl;
+ f_types_ << indent() << "return true, err" << endl;
+ } else {
+ f_types_ << endl;
+ f_types_ << indent() << "return true, nil" << endl;
+ }
+ indent_down();
+ f_types_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_go_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ bool declare,
+ string prefix,
+ bool inclass,
+ bool coerceData,
+ bool inkey,
+ bool in_container_value) {
+ (void)inclass;
+ (void)coerceData;
+ t_type* orig_type = tfield->get_type();
+ t_type* type = get_true_type(orig_type);
+ string name(prefix + publicize(tfield->get_name()));
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out,
+ (t_struct*)type,
+ is_pointer_field(tfield, in_container_value),
+ declare,
+ name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, orig_type, is_pointer_field(tfield), declare, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ if (declare) {
+ string type_name = inkey ? type_to_go_key_type(tfield->get_type())
+ : type_to_go_type(tfield->get_type());
+
+ out << "var " << tfield->get_name() << " " << type_name << endl;
+ }
+
+ indent(out) << "if v, err := iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary() && !inkey) {
+ out << "ReadBinary()";
+ } else {
+ out << "ReadString()";
+ }
+
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ out << "ReadBool()";
+ break;
+
+ case t_base_type::TYPE_I8:
+ out << "ReadByte()";
+ break;
+
+ case t_base_type::TYPE_I16:
+ out << "ReadI16()";
+ break;
+
+ case t_base_type::TYPE_I32:
+ out << "ReadI32()";
+ break;
+
+ case t_base_type::TYPE_I64:
+ out << "ReadI64()";
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ out << "ReadDouble()";
+ break;
+
+ default:
+ throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "ReadI32()";
+ }
+
+ out << "; err != nil {" << endl;
+ out << indent() << "return thrift.PrependError(\"error reading field " << tfield->get_key()
+ << ": \", err)" << endl;
+
+ out << "} else {" << endl;
+ string wrap;
+
+ if (type->is_enum() || orig_type->is_typedef()) {
+ wrap = publicize(type_name(orig_type));
+ } else if (((t_base_type*)type)->get_base() == t_base_type::TYPE_I8) {
+ wrap = "int8";
+ }
+
+ string maybe_address = (is_pointer_field(tfield) ? "&" : "");
+ if (wrap == "") {
+ indent(out) << name << " = " << maybe_address << "v" << endl;
+ } else {
+ indent(out) << "temp := " << wrap << "(v)" << endl;
+ indent(out) << name << " = " << maybe_address << "temp" << endl;
+ }
+
+ out << "}" << endl;
+ } else {
+ throw "INVALID TYPE IN generate_deserialize_field '" + type->get_name() + "' for field '"
+ + tfield->get_name() + "'";
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, calling read()
+ */
+void t_go_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ bool pointer_field,
+ bool declare,
+ string prefix) {
+ string eq(declare ? " := " : " = ");
+
+ out << indent() << prefix << eq << (pointer_field ? "&" : "");
+ generate_go_struct_initializer(out, tstruct);
+ out << indent() << "if err := " << prefix << "." << read_method_name_ << "(iprot); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", "
+ << prefix << "), err)" << endl;
+ out << indent() << "}" << endl;
+}
+
+/**
+ * Serialize a container by writing out the header followed by
+ * data and then a footer.
+ */
+void t_go_generator::generate_deserialize_container(ostream& out,
+ t_type* orig_type,
+ bool pointer_field,
+ bool declare,
+ string prefix) {
+ t_type* ttype = get_true_type(orig_type);
+ string eq(" = ");
+
+ if (declare) {
+ eq = " := ";
+ }
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ out << indent() << "_, _, size, err := iprot.ReadMapBegin()" << endl;
+ out << indent() << "if err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error reading map begin: \", err)" << endl;
+ out << indent() << "}" << endl;
+ out << indent() << "tMap := make(" << type_to_go_type(orig_type) << ", size)" << endl;
+ out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tMap" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "_, size, err := iprot.ReadSetBegin()" << endl;
+ out << indent() << "if err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error reading set begin: \", err)" << endl;
+ out << indent() << "}" << endl;
+ out << indent() << "tSet := make(" << type_to_go_type(orig_type) << ", 0, size)" << endl;
+ out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tSet" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "_, size, err := iprot.ReadListBegin()" << endl;
+ out << indent() << "if err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error reading list begin: \", err)" << endl;
+ out << indent() << "}" << endl;
+ out << indent() << "tSlice := make(" << type_to_go_type(orig_type) << ", 0, size)" << endl;
+ out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tSlice" << endl;
+ } else {
+ throw "INVALID TYPE IN generate_deserialize_container '" + ttype->get_name() + "' for prefix '"
+ + prefix + "'";
+ }
+
+ // For loop iterates over elements
+ out << indent() << "for i := 0; i < size; i ++ {" << endl;
+ indent_up();
+
+ if (pointer_field) {
+ prefix = "(*" + prefix + ")";
+ }
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, declare, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, declare, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, declare, prefix);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ // Read container end
+ if (ttype->is_map()) {
+ out << indent() << "if err := iprot.ReadMapEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error reading map end: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "if err := iprot.ReadSetEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error reading set end: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "if err := iprot.ReadListEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error reading list end: \", err)" << endl;
+ out << indent() << "}" << endl;
+ }
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_go_generator::generate_deserialize_map_element(ostream& out,
+ t_map* tmap,
+ bool declare,
+ string prefix) {
+ (void)declare;
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+ fkey.set_req(t_field::T_OPT_IN_REQ_OUT);
+ fval.set_req(t_field::T_OPT_IN_REQ_OUT);
+ generate_deserialize_field(out, &fkey, true, "", false, false, true);
+ generate_deserialize_field(out, &fval, true, "", false, false, false, true);
+ indent(out) << prefix << "[" << key << "] = " << val << endl;
+}
+
+/**
+ * Write a set element
+ */
+void t_go_generator::generate_deserialize_set_element(ostream& out,
+ t_set* tset,
+ bool declare,
+ string prefix) {
+ (void)declare;
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+ felem.set_req(t_field::T_OPT_IN_REQ_OUT);
+ generate_deserialize_field(out, &felem, true, "", false, false, false, true);
+ indent(out) << prefix << " = append(" << prefix << ", " << elem << ")" << endl;
+}
+
+/**
+ * Write a list element
+ */
+void t_go_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ bool declare,
+ string prefix) {
+ (void)declare;
+ string elem = tmp("_elem");
+ t_field felem(((t_list*)tlist)->get_elem_type(), elem);
+ felem.set_req(t_field::T_OPT_IN_REQ_OUT);
+ generate_deserialize_field(out, &felem, true, "", false, false, false, true);
+ indent(out) << prefix << " = append(" << prefix << ", " << elem << ")" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_go_generator::generate_serialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool inkey) {
+ t_type* type = get_true_type(tfield->get_type());
+ string name(prefix + publicize(tfield->get_name()));
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "compiler error: cannot generate serialize for void type: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, is_pointer_field(tfield), name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << "if err := oprot.";
+
+ if (is_pointer_field(tfield)) {
+ name = "*" + name;
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary() && !inkey) {
+ out << "WriteBinary(" << name << ")";
+ } else {
+ out << "WriteString(string(" << name << "))";
+ }
+
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ out << "WriteBool(bool(" << name << "))";
+ break;
+
+ case t_base_type::TYPE_I8:
+ out << "WriteByte(int8(" << name << "))";
+ break;
+
+ case t_base_type::TYPE_I16:
+ out << "WriteI16(int16(" << name << "))";
+ break;
+
+ case t_base_type::TYPE_I32:
+ out << "WriteI32(int32(" << name << "))";
+ break;
+
+ case t_base_type::TYPE_I64:
+ out << "WriteI64(int64(" << name << "))";
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ out << "WriteDouble(float64(" << name << "))";
+ break;
+
+ default:
+ throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "WriteI32(int32(" << name << "))";
+ }
+
+ out << "; err != nil {" << endl;
+ out << indent() << "return thrift.PrependError(fmt.Sprintf(\"%T."
+ << escape_string(tfield->get_name()) << " (" << tfield->get_key()
+ << ") field write error: \", p), err) }" << endl;
+ } else {
+ throw "compiler error: Invalid type in generate_serialize_field '" + type->get_name()
+ + "' for field '" + name + "'";
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_go_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ out << indent() << "if err := " << prefix << "." << write_method_name_ << "(oprot); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", "
+ << prefix << "), err)" << endl;
+ out << indent() << "}" << endl;
+}
+
+void t_go_generator::generate_serialize_container(ostream& out,
+ t_type* ttype,
+ bool pointer_field,
+ string prefix) {
+ if (pointer_field) {
+ prefix = "*" + prefix;
+ }
+ if (ttype->is_map()) {
+ out << indent() << "if err := oprot.WriteMapBegin("
+ << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "len(" << prefix << ")); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error writing map begin: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "if err := oprot.WriteSetBegin("
+ << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
+ << "len(" << prefix << ")); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error writing set begin: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "if err := oprot.WriteListBegin("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", "
+ << "len(" << prefix << ")); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error writing list begin: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else {
+ throw "compiler error: Invalid type in generate_serialize_container '" + ttype->get_name()
+ + "' for prefix '" + prefix + "'";
+ }
+
+ if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ out << indent() << "for k, v := range " << prefix << " {" << endl;
+ indent_up();
+ generate_serialize_map_element(out, tmap, "k", "v");
+ indent_down();
+ indent(out) << "}" << endl;
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ out << indent() << "for i := 0; i<len(" << prefix << "); i++ {" << endl;
+ out << indent() << " for j := i+1; j<len(" << prefix << "); j++ {" << endl;
+ string wrapped_prefix = prefix;
+ if (pointer_field) {
+ wrapped_prefix = "(" + prefix + ")";
+ }
+ out << indent() << " if reflect.DeepEqual(" << wrapped_prefix << "[i]," << wrapped_prefix << "[j]) { " << endl;
+ out << indent() << " return thrift.PrependError(\"\", fmt.Errorf(\"%T error writing set field: slice is not unique\", " << wrapped_prefix << "[i]))" << endl;
+ out << indent() << " }" << endl;
+ out << indent() << " }" << endl;
+ out << indent() << "}" << endl;
+ out << indent() << "for _, v := range " << prefix << " {" << endl;
+ indent_up();
+ generate_serialize_set_element(out, tset, "v");
+ indent_down();
+ indent(out) << "}" << endl;
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ out << indent() << "for _, v := range " << prefix << " {" << endl;
+
+ indent_up();
+ generate_serialize_list_element(out, tlist, "v");
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ if (ttype->is_map()) {
+ out << indent() << "if err := oprot.WriteMapEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error writing map end: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << "if err := oprot.WriteSetEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error writing set end: \", err)" << endl;
+ out << indent() << "}" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << "if err := oprot.WriteListEnd(); err != nil {" << endl;
+ out << indent() << " return thrift.PrependError(\"error writing list end: \", err)" << endl;
+ out << indent() << "}" << endl;
+ }
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_go_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), "");
+ t_field vfield(tmap->get_val_type(), "");
+ kfield.set_req(t_field::T_OPT_IN_REQ_OUT);
+ vfield.set_req(t_field::T_OPT_IN_REQ_OUT);
+ generate_serialize_field(out, &kfield, kiter, true);
+ generate_serialize_field(out, &vfield, viter);
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_go_generator::generate_serialize_set_element(ostream& out, t_set* tset, string prefix) {
+ t_field efield(tset->get_elem_type(), "");
+ efield.set_req(t_field::T_OPT_IN_REQ_OUT);
+ generate_serialize_field(out, &efield, prefix);
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_go_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string prefix) {
+ t_field efield(tlist->get_elem_type(), "");
+ efield.set_req(t_field::T_OPT_IN_REQ_OUT);
+ generate_serialize_field(out, &efield, prefix);
+}
+
+/**
+ * Generates the docstring for a given struct.
+ */
+void t_go_generator::generate_go_docstring(ostream& out, t_struct* tstruct) {
+ generate_go_docstring(out, tstruct, tstruct, "Attributes");
+}
+
+/**
+ * Generates the docstring for a given function.
+ */
+void t_go_generator::generate_go_docstring(ostream& out, t_function* tfunction) {
+ generate_go_docstring(out, tfunction, tfunction->get_arglist(), "Parameters");
+}
+
+/**
+ * Generates the docstring for a struct or function.
+ */
+void t_go_generator::generate_go_docstring(ostream& out,
+ t_doc* tdoc,
+ t_struct* tstruct,
+ const char* subheader) {
+ bool has_doc = false;
+ stringstream ss;
+
+ if (tdoc->has_doc()) {
+ has_doc = true;
+ ss << tdoc->get_doc();
+ }
+
+ const vector<t_field*>& fields = tstruct->get_members();
+
+ if (fields.size() > 0) {
+ if (has_doc) {
+ ss << endl;
+ }
+
+ has_doc = true;
+ ss << subheader << ":\n";
+ vector<t_field*>::const_iterator p_iter;
+
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ss << " - " << publicize(p->get_name());
+
+ if (p->has_doc()) {
+ ss << ": " << p->get_doc();
+ } else {
+ ss << endl;
+ }
+ }
+ }
+
+ if (has_doc) {
+ generate_docstring_comment(out, "", "// ", ss.str(), "");
+ }
+}
+
+/**
+ * Generates the docstring for a generic object.
+ */
+void t_go_generator::generate_go_docstring(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_docstring_comment(out, "", "//", tdoc->get_doc(), "");
+ }
+}
+
+/**
+ * Declares an argument, which may include initialization as necessary.
+ *
+ * @param tfield The field
+ */
+string t_go_generator::declare_argument(t_field* tfield) {
+ std::ostringstream result;
+ result << publicize(tfield->get_name()) << "=";
+
+ if (tfield->get_value() != NULL) {
+ result << "thrift_spec[" << tfield->get_key() << "][4]";
+ } else {
+ result << "nil";
+ }
+
+ return result.str();
+}
+
+/**
+ * Renders a struct field initial value.
+ *
+ * @param tfield The field, which must have `tfield->get_value() != NULL`
+ */
+string t_go_generator::render_field_initial_value(t_field* tfield,
+ const string& name,
+ bool optional_field) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (optional_field) {
+ // The caller will make a second pass for optional fields,
+ // assigning the result of render_const_value to "*field_name". It
+ // is maddening that Go syntax does not allow for a type-agnostic
+ // way to initialize a pointer to a const value, but so it goes.
+ // The alternative would be to write type specific functions that
+ // convert from const values to pointer types, but given the lack
+ // of overloading it would be messy.
+ return "new(" + type_to_go_type(tfield->get_type()) + ")";
+ } else {
+ return render_const_value(type, tfield->get_value(), name);
+ }
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_go_generator::function_signature(t_function* tfunction, string prefix) {
+ // TODO(mcslee): Nitpicky, no ',' if argument_list is empty
+ return publicize(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist())
+ + ")";
+}
+
+/**
+ * Renders an interface function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_go_generator::function_signature_if(t_function* tfunction, string prefix, bool addError) {
+ string signature = publicize(prefix + tfunction->get_name()) + "(";
+ signature += "ctx context.Context";
+ if (!tfunction->get_arglist()->get_members().empty()) {
+ signature += ", " + argument_list(tfunction->get_arglist());
+ }
+ signature += ") (";
+
+ t_type* ret = tfunction->get_returntype();
+ t_struct* exceptions = tfunction->get_xceptions();
+ string errs = argument_list(exceptions);
+
+ if (!ret->is_void()) {
+ signature += "r " + type_to_go_type(ret);
+
+ if (addError || errs.size() == 0) {
+ signature += ", ";
+ }
+ }
+
+ if (addError) {
+ signature += "err error";
+ }
+
+ signature += ")";
+ return signature;
+}
+
+/**
+ * Renders a field list
+ */
+string t_go_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+
+ result += variable_name_to_go_name((*f_iter)->get_name()) + " "
+ + type_to_go_type((*f_iter)->get_type());
+ }
+
+ return result;
+}
+
+string t_go_generator::type_name(t_type* ttype) {
+ string module( module_name(ttype));
+ if( ! module.empty()) {
+ return module + "." + ttype->get_name();
+ }
+
+ return ttype->get_name();
+}
+
+string t_go_generator::module_name(t_type* ttype) {
+ t_program* program = ttype->get_program();
+
+ if (program != NULL && program != program_) {
+ if (program->get_namespace("go").empty() ||
+ program_->get_namespace("go").empty() ||
+ program->get_namespace("go") != program_->get_namespace("go")) {
+ string module(get_real_go_module(program));
+ return package_identifiers_[module];
+ }
+ }
+
+ return "";
+}
+
+/**
+ * Converts the parse type to a go tyoe
+ */
+string t_go_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+
+ case t_base_type::TYPE_STRING:
+ /* this is wrong, binary is still a string type internally
+ if (type->is_binary()) {
+ return "thrift.BINARY";
+ }
+ */
+ return "thrift.STRING";
+
+ case t_base_type::TYPE_BOOL:
+ return "thrift.BOOL";
+
+ case t_base_type::TYPE_I8:
+ return "thrift.BYTE";
+
+ case t_base_type::TYPE_I16:
+ return "thrift.I16";
+
+ case t_base_type::TYPE_I32:
+ return "thrift.I32";
+
+ case t_base_type::TYPE_I64:
+ return "thrift.I64";
+
+ case t_base_type::TYPE_DOUBLE:
+ return "thrift.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "thrift.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "thrift.STRUCT";
+ } else if (type->is_map()) {
+ return "thrift.MAP";
+ } else if (type->is_set()) {
+ return "thrift.SET";
+ } else if (type->is_list()) {
+ return "thrift.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Converts the parse type to a go map type, will throw an exception if it will
+ * not produce a valid go map type.
+ */
+string t_go_generator::type_to_go_key_type(t_type* type) {
+ t_type* resolved_type = type;
+
+ while (resolved_type->is_typedef()) {
+ resolved_type = ((t_typedef*)resolved_type)->get_type()->get_true_type();
+ }
+
+ if (resolved_type->is_map() || resolved_type->is_list() || resolved_type->is_set()) {
+ throw "Cannot produce a valid type for a Go map key: " + type_to_go_type(type) + " - aborting.";
+ }
+
+ if (resolved_type->is_binary())
+ return "string";
+
+ return type_to_go_type(type);
+}
+
+/**
+ * Converts the parse type to a go type
+ */
+string t_go_generator::type_to_go_type(t_type* type) {
+ return type_to_go_type_with_opt(type, false);
+}
+
+/**
+ * Converts the parse type to a go type, taking into account whether the field
+ * associated with the type is T_OPTIONAL.
+ */
+string t_go_generator::type_to_go_type_with_opt(t_type* type,
+ bool optional_field) {
+ string maybe_pointer(optional_field ? "*" : "");
+
+ if (type->is_typedef() && ((t_typedef*)type)->is_forward_typedef()) {
+ type = ((t_typedef*)type)->get_true_type();
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "";
+
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ return maybe_pointer + "[]byte";
+ }
+
+ return maybe_pointer + "string";
+
+ case t_base_type::TYPE_BOOL:
+ return maybe_pointer + "bool";
+
+ case t_base_type::TYPE_I8:
+ return maybe_pointer + "int8";
+
+ case t_base_type::TYPE_I16:
+ return maybe_pointer + "int16";
+
+ case t_base_type::TYPE_I32:
+ return maybe_pointer + "int32";
+
+ case t_base_type::TYPE_I64:
+ return maybe_pointer + "int64";
+
+ case t_base_type::TYPE_DOUBLE:
+ return maybe_pointer + "float64";
+ }
+ } else if (type->is_enum()) {
+ return maybe_pointer + publicize(type_name(type));
+ } else if (type->is_struct() || type->is_xception()) {
+ return "*" + publicize(type_name(type));
+ } else if (type->is_map()) {
+ t_map* t = (t_map*)type;
+ string keyType = type_to_go_key_type(t->get_key_type());
+ string valueType = type_to_go_type(t->get_val_type());
+ return maybe_pointer + string("map[") + keyType + "]" + valueType;
+ } else if (type->is_set()) {
+ t_set* t = (t_set*)type;
+ string elemType = type_to_go_type(t->get_elem_type());
+ return maybe_pointer + string("[]") + elemType;
+ } else if (type->is_list()) {
+ t_list* t = (t_list*)type;
+ string elemType = type_to_go_type(t->get_elem_type());
+ return maybe_pointer + string("[]") + elemType;
+ } else if (type->is_typedef()) {
+ return maybe_pointer + publicize(type_name(type));
+ }
+
+ throw "INVALID TYPE IN type_to_go_type: " + type->get_name();
+}
+
+/** See the comment inside generate_go_struct_definition for what this is. */
+string t_go_generator::type_to_spec_args(t_type* ttype) {
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ if (ttype->is_base_type() || ttype->is_enum()) {
+ return "nil";
+ } else if (ttype->is_struct() || ttype->is_xception()) {
+ return "(" + type_name(ttype) + ", " + type_name(ttype) + ".thrift_spec)";
+ } else if (ttype->is_map()) {
+ return "(" + type_to_enum(((t_map*)ttype)->get_key_type()) + ","
+ + type_to_spec_args(((t_map*)ttype)->get_key_type()) + ","
+ + type_to_enum(((t_map*)ttype)->get_val_type()) + ","
+ + type_to_spec_args(((t_map*)ttype)->get_val_type()) + ")";
+ } else if (ttype->is_set()) {
+ return "(" + type_to_enum(((t_set*)ttype)->get_elem_type()) + ","
+ + type_to_spec_args(((t_set*)ttype)->get_elem_type()) + ")";
+ } else if (ttype->is_list()) {
+ return "(" + type_to_enum(((t_list*)ttype)->get_elem_type()) + ","
+ + type_to_spec_args(((t_list*)ttype)->get_elem_type()) + ")";
+ }
+
+ throw "INVALID TYPE IN type_to_spec_args: " + ttype->get_name();
+}
+
+bool format_go_output(const string& file_path) {
+
+ // formatting via gofmt deactivated due to THRIFT-3893
+ // Please look at the ticket and make sure you fully understand all the implications
+ // before submitting a patch that enables this feature again. Thank you.
+ (void) file_path;
+ return false;
+
+ /*
+ const string command = "gofmt -w " + file_path;
+
+ if (system(command.c_str()) == 0) {
+ return true;
+ }
+
+ fprintf(stderr, "WARNING - Running '%s' failed.\n", command.c_str());
+ return false;
+ */
+ }
+
+THRIFT_REGISTER_GENERATOR(go, "Go",
+ " package_prefix= Package prefix for generated files.\n" \
+ " thrift_import= Override thrift package import path (default:" + DEFAULT_THRIFT_IMPORT + ")\n" \
+ " package= Package name (default: inferred from thrift file name)\n" \
+ " ignore_initialisms\n"
+ " Disable automatic spelling correction of initialisms (e.g. \"URL\")\n" \
+ " read_write_private\n"
+ " Make read/write methods private, default is public Read/Write\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc
new file mode 100644
index 000000000..2c0acf979
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <list>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::pair;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Graphviz code generator
+ */
+class t_gv_generator : public t_generator {
+public:
+ t_gv_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ exception_arrows = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("exceptions") == 0) {
+ exception_arrows = true;
+ } else {
+ throw "unknown option gv:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-gv";
+ }
+
+ /**
+ * Init and end of generator
+ */
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_service(t_service* tservice) override;
+
+protected:
+ /**
+ * Helpers
+ */
+ void print_type(t_type* ttype, string struct_field_ref);
+ void print_const_value(t_type* type, t_const_value* tvalue);
+
+private:
+ ofstream_with_content_based_conditional_update f_out_;
+ std::list<string> edges;
+ bool exception_arrows;
+};
+
+/**
+ * Init generator:
+ * - Adds some escaping for the Graphviz domain.
+ * - Create output directory and open file for writting.
+ * - Write the file header.
+ */
+void t_gv_generator::init_generator() {
+ escape_['{'] = "\\{";
+ escape_['}'] = "\\}";
+
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+ string fname = get_out_dir() + program_->get_name() + ".gv";
+ f_out_.open(fname.c_str());
+ f_out_ << "digraph \"" << escape_string(program_name_) << "\" {" << endl;
+ f_out_ << "node [style=filled, shape=record];" << endl;
+ f_out_ << "edge [arrowsize=0.5];" << endl;
+ f_out_ << "rankdir=LR" << endl;
+}
+
+/**
+ * Closes generator:
+ * - Print accumulated nodes connections.
+ * - Print footnote.
+ * - Closes file.
+ */
+void t_gv_generator::close_generator() {
+ // Print edges
+ std::list<string>::iterator iter = edges.begin();
+ for (; iter != edges.end(); iter++) {
+ f_out_ << (*iter) << endl;
+ }
+
+ // Print graph end } and close file
+ f_out_ << "}" << endl;
+ f_out_.close();
+}
+
+void t_gv_generator::generate_typedef(t_typedef* ttypedef) {
+ string name = ttypedef->get_name();
+ f_out_ << "node [fillcolor=azure];" << endl;
+ f_out_ << name << " [label=\"";
+
+ f_out_ << escape_string(name);
+ f_out_ << " :: ";
+ print_type(ttypedef->get_type(), name);
+
+ f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::generate_enum(t_enum* tenum) {
+ string name = tenum->get_name();
+ f_out_ << "node [fillcolor=white];" << endl;
+ f_out_ << name << " [label=\"enum " << escape_string(name);
+
+ vector<t_enum_value*> values = tenum->get_constants();
+ vector<t_enum_value*>::iterator val_iter;
+ for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
+ f_out_ << '|' << (*val_iter)->get_name();
+ f_out_ << " = ";
+ f_out_ << (*val_iter)->get_value();
+ }
+
+ f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::generate_const(t_const* tconst) {
+ string name = tconst->get_name();
+
+ f_out_ << "node [fillcolor=aliceblue];" << endl;
+ f_out_ << "const_" << name << " [label=\"";
+
+ f_out_ << escape_string(name);
+ f_out_ << " = ";
+ print_const_value(tconst->get_type(), tconst->get_value());
+ f_out_ << " :: ";
+ print_type(tconst->get_type(), "const_" + name);
+
+ f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::generate_struct(t_struct* tstruct) {
+ string name = tstruct->get_name();
+
+ if (tstruct->is_xception()) {
+ f_out_ << "node [fillcolor=lightpink];" << endl;
+ f_out_ << name << " [label=\"";
+ f_out_ << "exception " << escape_string(name);
+ } else if (tstruct->is_union()) {
+ f_out_ << "node [fillcolor=lightcyan];" << endl;
+ f_out_ << name << " [label=\"";
+ f_out_ << "union " << escape_string(name);
+ } else {
+ f_out_ << "node [fillcolor=beige];" << endl;
+ f_out_ << name << " [label=\"";
+ f_out_ << "struct " << escape_string(name);
+ }
+
+ vector<t_field*> members = tstruct->get_members();
+ vector<t_field*>::iterator mem_iter = members.begin();
+ for (; mem_iter != members.end(); mem_iter++) {
+ string field_name = (*mem_iter)->get_name();
+
+ // print port (anchor reference)
+ f_out_ << "|<field_" << field_name << '>';
+
+ // field name :: field type
+ f_out_ << (*mem_iter)->get_name();
+ f_out_ << " :: ";
+ print_type((*mem_iter)->get_type(), name + ":field_" + field_name);
+ }
+
+ f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::print_type(t_type* ttype, string struct_field_ref) {
+ if (ttype->is_container()) {
+ if (ttype->is_list()) {
+ f_out_ << "list\\<";
+ print_type(((t_list*)ttype)->get_elem_type(), struct_field_ref);
+ f_out_ << "\\>";
+ } else if (ttype->is_set()) {
+ f_out_ << "set\\<";
+ print_type(((t_set*)ttype)->get_elem_type(), struct_field_ref);
+ f_out_ << "\\>";
+ } else if (ttype->is_map()) {
+ f_out_ << "map\\<";
+ print_type(((t_map*)ttype)->get_key_type(), struct_field_ref);
+ f_out_ << ", ";
+ print_type(((t_map*)ttype)->get_val_type(), struct_field_ref);
+ f_out_ << "\\>";
+ }
+ } else if (ttype->is_base_type()) {
+ f_out_ << (ttype->is_binary() ? "binary" : ttype->get_name());
+ } else {
+ f_out_ << ttype->get_name();
+ edges.push_back(struct_field_ref + " -> " + ttype->get_name());
+ }
+}
+
+/**
+ * Prints out an string representation of the provided constant value
+ */
+void t_gv_generator::print_const_value(t_type* type, t_const_value* tvalue) {
+ bool first = true;
+ switch (tvalue->get_type()) {
+ case t_const_value::CV_INTEGER:
+ f_out_ << tvalue->get_integer();
+ break;
+ case t_const_value::CV_DOUBLE:
+ f_out_ << tvalue->get_double();
+ break;
+ case t_const_value::CV_STRING:
+ f_out_ << "\\\"" << get_escaped_string(tvalue) << "\\\"";
+ break;
+ case t_const_value::CV_MAP: {
+ f_out_ << "\\{ ";
+ map<t_const_value*, t_const_value*, t_const_value::value_compare> map_elems = tvalue->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator map_iter;
+ for (map_iter = map_elems.begin(); map_iter != map_elems.end(); map_iter++) {
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ print_const_value(((t_map*)type)->get_key_type(), map_iter->first);
+ f_out_ << " = ";
+ print_const_value(((t_map*)type)->get_val_type(), map_iter->second);
+ }
+ f_out_ << " \\}";
+ } break;
+ case t_const_value::CV_LIST: {
+ f_out_ << "\\{ ";
+ vector<t_const_value*> list_elems = tvalue->get_list();
+ ;
+ vector<t_const_value*>::iterator list_iter;
+ for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ if (type->is_list()) {
+ print_const_value(((t_list*)type)->get_elem_type(), *list_iter);
+ } else {
+ print_const_value(((t_set*)type)->get_elem_type(), *list_iter);
+ }
+ }
+ f_out_ << " \\}";
+ } break;
+ case t_const_value::CV_IDENTIFIER:
+ f_out_ << escape_string(type->get_name()) << "."
+ << escape_string(tvalue->get_identifier_name());
+ break;
+ default:
+ f_out_ << "UNKNOWN";
+ break;
+ }
+}
+
+void t_gv_generator::generate_service(t_service* tservice) {
+ string service_name = get_service_name(tservice);
+ f_out_ << "subgraph cluster_" << service_name << " {" << endl;
+ f_out_ << "node [fillcolor=bisque];" << endl;
+ f_out_ << "style=dashed;" << endl;
+ f_out_ << "label = \"" << escape_string(service_name) << " service\";" << endl;
+
+ // TODO: service extends
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator fn_iter = functions.begin();
+ for (; fn_iter != functions.end(); fn_iter++) {
+ string fn_name = (*fn_iter)->get_name();
+
+ f_out_ << "function_" << service_name << fn_name;
+ f_out_ << "[label=\"<return_type>function " << escape_string(fn_name);
+ f_out_ << " :: ";
+ print_type((*fn_iter)->get_returntype(), "function_" + service_name + fn_name + ":return_type");
+
+ vector<t_field*> args = (*fn_iter)->get_arglist()->get_members();
+ vector<t_field*>::iterator arg_iter = args.begin();
+ for (; arg_iter != args.end(); arg_iter++) {
+ f_out_ << "|<param_" << (*arg_iter)->get_name() << ">";
+ f_out_ << (*arg_iter)->get_name();
+ if ((*arg_iter)->get_value() != NULL) {
+ f_out_ << " = ";
+ print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value());
+ }
+ f_out_ << " :: ";
+ print_type((*arg_iter)->get_type(),
+ "function_" + service_name + fn_name + ":param_" + (*arg_iter)->get_name());
+ }
+ // end of node
+ f_out_ << "\"];" << endl;
+
+ // Exception edges
+ if (exception_arrows) {
+ vector<t_field*> excepts = (*fn_iter)->get_xceptions()->get_members();
+ vector<t_field*>::iterator ex_iter = excepts.begin();
+ for (; ex_iter != excepts.end(); ex_iter++) {
+ edges.push_back("function_" + service_name + fn_name + " -> "
+ + (*ex_iter)->get_type()->get_name() + " [color=red]");
+ }
+ }
+ }
+
+ f_out_ << " }" << endl;
+}
+
+THRIFT_REGISTER_GENERATOR(
+ gv,
+ "Graphviz",
+ " exceptions: Whether to draw arrows from functions to exception.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc
new file mode 100644
index 000000000..13f2cd1ba
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc
@@ -0,0 +1,2981 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <sys/stat.h>
+#include <stdexcept>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Haxe code generator.
+ *
+ */
+class t_haxe_generator : public t_oop_generator {
+public:
+ t_haxe_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ callbacks_ = false;
+ rtti_ = false;
+ buildmacro_ = "";
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("callbacks") == 0) {
+ callbacks_ = true;
+ } else if( iter->first.compare("rtti") == 0) {
+ rtti_ = true;
+ } else if( iter->first.compare("buildmacro") == 0) {
+ buildmacro_ = (iter->second);
+ } else {
+ throw "unknown option haxe:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-haxe";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ void print_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval = false);
+ std::string render_const_value(ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_haxe_struct(t_struct* tstruct, bool is_exception, bool is_result = false);
+
+ void generate_haxe_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool is_result = false);
+ // removed -- equality,compare_to
+ void generate_haxe_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_haxe_validator(std::ostream& out, t_struct* tstruct);
+ void generate_haxe_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_haxe_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_haxe_struct_tostring(std::ostream& out, t_struct* tstruct);
+ void generate_haxe_meta_data_map(std::ostream& out, t_struct* tstruct);
+ void generate_field_value_meta_data(std::ostream& out, t_type* type);
+ std::string get_haxe_type_string(t_type* type);
+ void generate_reflection_setters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_reflection_getters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct);
+ void generate_generic_isset_method(std::ostream& out, t_struct* tstruct);
+ void generate_property_getters_setters(std::ostream& out, t_struct* tstruct);
+
+ void generate_function_helpers(t_function* tfunction);
+ std::string get_cap_name(std::string name);
+ std::string generate_isset_check(t_field* field);
+ std::string generate_isset_check(std::string field);
+ void generate_isset_set(ostream& out, t_field* field);
+ // removed std::string isset_field_id(t_field* field);
+
+ void generate_service_interface(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+ void generate_service_method_signature(t_function* tfunction, bool is_interface);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string iter,
+ std::string map);
+
+ void generate_haxe_doc(std::ostream& out, t_doc* tdoc);
+ void generate_haxe_doc(std::ostream& out, t_function* tdoc);
+
+ void generate_rtti_decoration(std::ostream& out);
+ void generate_macro_decoration(std::ostream& out);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string haxe_package();
+ std::string haxe_type_imports();
+ std::string haxe_thrift_imports();
+ std::string haxe_thrift_gen_imports(t_struct* tstruct, string& imports);
+ std::string haxe_thrift_gen_imports(t_service* tservice);
+ std::string type_name(t_type* ttype, bool in_container = false, bool in_init = false);
+ std::string base_type_name(t_base_type* tbase, bool in_container = false);
+ std::string declare_field(t_field* tfield, bool init = false);
+ std::string function_signature_callback(t_function* tfunction);
+ std::string function_signature_normal(t_function* tfunction);
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string get_enum_class_name(t_type* type) override;
+ string generate_service_method_onsuccess(t_function* tfunction, bool as_type, bool omit_name);
+ void generate_service_method_signature_callback(t_function* tfunction, bool is_interface);
+ void generate_service_method_signature_normal(t_function* tfunction, bool is_interface);
+
+ bool type_can_be_null(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ if (ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()) {
+ return true;
+ }
+
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ // case t_base_type::TYPE_I64: - Int64 is not really nullable, even though it behaved that
+ // way before Haxe 3.2.0
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ std::string constant_name(std::string name);
+
+private:
+ bool callbacks_;
+ bool rtti_;
+ string buildmacro_;
+
+ /**
+ * File streams
+ */
+
+ std::string package_name_;
+ ofstream_with_content_based_conditional_update f_service_;
+ std::string package_dir_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_haxe_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+ package_name_ = program_->get_namespace("haxe");
+
+ // Haxe package names are lowercase
+ if (package_name_.length() > 0) {
+ package_name_[0] = tolower(package_name_[0]);
+ size_t index = package_name_.find('.');
+ while (index != std::string::npos) {
+ if (++index < package_name_.length()) {
+ package_name_[index] = tolower(package_name_[index]);
+ }
+ index = package_name_.find('.', index);
+ }
+ }
+
+ string dir = package_name_;
+ string subdir = get_out_dir();
+ string::size_type loc;
+ while ((loc = dir.find(".")) != string::npos) {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0) {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ package_dir_ = subdir;
+}
+
+/**
+ * Packages the generated file
+ *
+ * @return String of the package, i.e. "package org.apache.thriftdemo;"
+ */
+string t_haxe_generator::haxe_package() {
+ if (!package_name_.empty()) {
+ return string("package ") + package_name_;
+ }
+ return "package";
+}
+
+/**
+ * Prints standard haxe imports
+ *
+ * @return List of imports for haxe types that are used in here
+ */
+string t_haxe_generator::haxe_type_imports() {
+ return string() + "import org.apache.thrift.helper.*;\n" + "import haxe.io.Bytes;\n"
+ + "import haxe.ds.IntMap;\n" + "import haxe.ds.StringMap;\n"
+ + "import haxe.ds.ObjectMap;\n" + "\n" + "#if flash\n"
+ + "import flash.errors.ArgumentError;\n" + "#end\n" + "\n";
+}
+
+/**
+ * Prints standard haxe imports
+ *
+ * @return List of imports necessary for thrift
+ */
+string t_haxe_generator::haxe_thrift_imports() {
+ return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n"
+ + "import org.apache.thrift.protocol.*;\n" + "\n";
+}
+
+/**
+ * Prints imports needed for a given type
+ *
+ * @return List of imports necessary for a given t_struct
+ */
+string t_haxe_generator::haxe_thrift_gen_imports(t_struct* tstruct, string& imports) {
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // For each type check if it is from a different namespace
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_program* program = (*m_iter)->get_type()->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("haxe");
+ if (!package.empty()) {
+ if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) {
+ imports.append("import " + package + "." + (*m_iter)->get_type()->get_name() + ";\n");
+ }
+ }
+ }
+ }
+ return imports;
+}
+
+/**
+ * Prints imports needed for a given type
+ *
+ * @return List of imports necessary for a given t_service
+ */
+string t_haxe_generator::haxe_thrift_gen_imports(t_service* tservice) {
+ string imports;
+ const vector<t_function*>& functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+
+ // For each type check if it is from a different namespace
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_program* program = (*f_iter)->get_returntype()->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("haxe");
+ if (!package.empty()) {
+ if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) {
+ imports.append("import " + package + "." + (*f_iter)->get_returntype()->get_name()
+ + ";\n");
+ }
+ }
+ }
+
+ haxe_thrift_gen_imports((*f_iter)->get_arglist(), imports);
+ haxe_thrift_gen_imports((*f_iter)->get_xceptions(), imports);
+ }
+
+ return imports;
+}
+
+/**
+ * Nothing in haxe
+ */
+void t_haxe_generator::close_generator() {
+}
+
+/**
+ * Generates a typedef. This is not done in haxe, since it does
+ * not support arbitrary name replacements, and it'd be a wacky waste
+ * of overhead to make wrapper classes.
+ *
+ * @param ttypedef The type definition
+ */
+void t_haxe_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Enums are a class with a set of static constants.
+ *
+ * @param tenum The enumeration
+ */
+void t_haxe_generator::generate_enum(t_enum* tenum) {
+ // Make output file
+ string f_enum_name = package_dir_ + "/" + get_cap_name(tenum->get_name()) + ".hx";
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ // Comment and package it
+ f_enum << autogen_comment() << haxe_package() << ";" << endl << endl;
+
+ // Add haxe imports
+ f_enum << string() + "import org.apache.thrift.helper.*;" << endl << endl;
+
+ generate_rtti_decoration(f_enum);
+ generate_macro_decoration(f_enum);
+ indent(f_enum) << "class " << get_cap_name(tenum->get_name()) << " ";
+ scope_up(f_enum);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << "public static inline var " << (*c_iter)->get_name() << " : Int = " << value
+ << ";" << endl;
+ }
+
+ // Create a static Set with all valid values for this enum
+ f_enum << endl;
+
+ indent(f_enum) << "public static var VALID_VALUES = { new IntSet( [";
+ indent_up();
+ bool firstValue = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ // populate set
+ f_enum << (firstValue ? "" : ", ") << (*c_iter)->get_name();
+ firstValue = false;
+ }
+ indent_down();
+ f_enum << "]); };" << endl;
+
+ indent(f_enum) << "public static var VALUES_TO_NAMES = { [";
+ indent_up();
+ firstValue = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ f_enum << (firstValue ? "" : ",") << endl;
+ indent(f_enum) << (*c_iter)->get_name() << " => \"" << (*c_iter)->get_name() << "\"";
+ firstValue = false;
+ }
+ f_enum << endl;
+ indent_down();
+ indent(f_enum) << "]; };" << endl;
+
+ scope_down(f_enum); // end class
+
+ f_enum.close();
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_haxe_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+
+ string f_consts_name = package_dir_ + "/" + get_cap_name(program_name_) + "Constants.hx";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ // Print header
+ f_consts << autogen_comment() << haxe_package() << ";" << endl << endl;
+
+ f_consts << endl;
+
+ f_consts << haxe_type_imports();
+
+ generate_rtti_decoration(f_consts);
+ generate_macro_decoration(f_consts);
+ indent(f_consts) << "class " << get_cap_name(program_name_) << "Constants {" << endl << endl;
+ indent_up();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ print_const_value(f_consts,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ false);
+ }
+ indent_down();
+ indent(f_consts) << "}" << endl;
+ f_consts.close();
+}
+
+void t_haxe_generator::print_const_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval) {
+ type = get_true_type(type);
+
+ indent(out);
+ if (!defval) {
+ out << (in_static ? "var " : "public static inline var ");
+ }
+ if (type->is_base_type()) {
+ string v2 = render_const_value(out, name, type, value);
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = " << v2 << ";" << endl << endl;
+ } else if (type->is_enum()) {
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = " << value->get_integer() << ";" << endl << endl;
+ } else if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ out << name << ":" << type_name(type) << " = new " << type_name(type, false, true) << "();"
+ << endl;
+ if (!in_static) {
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "new function() : Void {" << endl;
+ indent_up();
+ }
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ indent(out) << name << ".";
+ out << v_iter->first->get_string() << " = " << val << ";" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_map()) {
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "new function() : Void {" << endl;
+ indent_up();
+ }
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ indent(out) << name << "[" << key << "] = " << val << ";" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_list() || type->is_set()) {
+ out << name;
+ if (!defval) {
+ out << ":" << type_name(type);
+ }
+ out << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "new function() : Void {" << endl;
+ indent_up();
+ }
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ indent(out) << name << "." << (type->is_list() ? "push" : "add") << "(" << val << ");"
+ << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else {
+ throw "compiler error: no const of type " + type->get_name();
+ }
+}
+
+string t_haxe_generator::render_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+ type = get_true_type(type);
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ render << "(byte)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I16:
+ render << "(short)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ render << value->get_integer() << "L";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << "(double)" << value->get_integer();
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << value->get_integer();
+ } else {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is a class
+ * with data members, read(), write(), and an inner Isset class.
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_struct(t_struct* tstruct) {
+ generate_haxe_struct(tstruct, false);
+}
+
+/**
+ * Exceptions are structs, but they inherit from Exception
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_xception(t_struct* txception) {
+ generate_haxe_struct(txception, true);
+}
+
+/**
+ * Haxe struct definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, bool is_exception, bool is_result) {
+ // Make output file
+ string f_struct_name = package_dir_ + "/" + get_cap_name(tstruct->get_name()) + ".hx";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << haxe_package() << ";" << endl;
+
+ f_struct << endl;
+
+ string imports;
+
+ f_struct << haxe_type_imports() << haxe_thrift_imports()
+ << haxe_thrift_gen_imports(tstruct, imports) << endl;
+
+ generate_haxe_struct_definition(f_struct, tstruct, is_exception, is_result);
+
+ f_struct.close();
+}
+
+/**
+ * haxe struct definition. This has various parameters, as it could be
+ * generated standalone or inside another class as a helper. If it
+ * is a helper than it is a static class.
+ *
+ * @param tstruct The struct definition
+ * @param is_exception Is this an exception?
+ * @param in_class If inside a class, needs to be static class
+ * @param is_result If this is a result it needs a different writer
+ */
+void t_haxe_generator::generate_haxe_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result) {
+ generate_haxe_doc(out, tstruct);
+
+ string clsname = get_cap_name(tstruct->get_name());
+
+ generate_rtti_decoration(out);
+ generate_macro_decoration(out);
+ indent(out) << "class " << clsname << " ";
+
+ if (is_exception) {
+ out << "extends TException ";
+ }
+ out << "implements TBase ";
+
+ scope_up(out);
+ indent(out) << endl;
+
+ indent(out) << "static var STRUCT_DESC = { new TStruct(\"" << tstruct->get_name() << "\"); };"
+ << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "static var " << constant_name((*m_iter)->get_name())
+ << "_FIELD_DESC = { new TField(\"" << (*m_iter)->get_name() << "\", "
+ << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << "); };"
+ << endl;
+ }
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_haxe_doc(out, *m_iter);
+ // indent(out) << "private var _" << (*m_iter)->get_name() + " : " +
+ // type_name((*m_iter)->get_type()) << ";" << endl;
+ indent(out) << "@:isVar" << endl;
+ indent(out) << "public var "
+ << (*m_iter)->get_name() + "(get,set) : "
+ + get_cap_name(type_name((*m_iter)->get_type())) << ";" << endl;
+ }
+
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "inline static var " << upcase_string((*m_iter)->get_name())
+ << "_FIELD_ID : Int = " << (*m_iter)->get_key() << ";" << endl;
+ }
+
+ out << endl;
+
+ // Inner Isset class
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!type_can_be_null((*m_iter)->get_type())) {
+ indent(out) << "private var __isset_" << (*m_iter)->get_name() << " : Bool = false;"
+ << endl;
+ }
+ }
+ }
+
+ out << endl;
+
+ // Static initializer to populate global class to struct metadata map
+ if (false) {
+ // TODO: reactivate when needed
+ generate_haxe_meta_data_map(out, tstruct);
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "FieldMetaData.addStructMetaDataMap(" << type_name(tstruct) << ", metaDataMap);"
+ << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent(out) << "}" << endl;
+ }
+
+ // Default constructor
+ indent(out) << "public function new() {" << endl;
+ indent_up();
+ if (is_exception) {
+ indent(out) << "super();" << endl;
+ }
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_value() != NULL) {
+ indent(out) << "this." << (*m_iter)->get_name() << " = "
+ << (*m_iter)->get_value()->get_integer() << ";" << endl;
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ generate_property_getters_setters(out, tstruct);
+ generate_generic_field_getters_setters(out, tstruct);
+ generate_generic_isset_method(out, tstruct);
+
+ generate_haxe_struct_reader(out, tstruct);
+ if (is_result) {
+ generate_haxe_struct_result_writer(out, tstruct);
+ } else {
+ generate_haxe_struct_writer(out, tstruct);
+ }
+ generate_haxe_struct_tostring(out, tstruct);
+ generate_haxe_validator(out, tstruct);
+ scope_down(out);
+ out << endl;
+}
+
+/**
+ * Generates a function to read all the fields of the struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_haxe_struct_reader(ostream& out, t_struct* tstruct) {
+ out << indent() << "public function read( iprot : TProtocol) : Void {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "iprot.IncrementRecursionDepth();" << endl;
+ indent(out) << "try" << endl;
+ scope_up(out);
+
+ // Declare stack tmp variables and read struct header
+ out << indent() << "var field : TField;" << endl << indent() << "iprot.readStructBegin();"
+ << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true)" << endl;
+ scope_up(out);
+
+ // Read beginning field marker
+ indent(out) << "field = iprot.readFieldBegin();" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if (field.type == TType.STOP) { " << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Switch statement on the field we are reading
+ indent(out) << "switch (field.id)" << endl;
+
+ scope_up(out);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << upcase_string((*f_iter)->get_name()) << "_FIELD_ID:" << endl;
+ indent_up();
+ indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter, "this.");
+ generate_isset_set(out, *f_iter);
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " TProtocolUtil.skip(iprot, field.type);"
+ << endl << indent() << "}" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ out << indent() << "default:" << endl << indent() << " TProtocolUtil.skip(iprot, field.type);"
+ << endl;
+
+ scope_down(out);
+
+ // Read field end marker
+ indent(out) << "iprot.readFieldEnd();" << endl;
+
+ scope_down(out);
+
+ out << indent() << "iprot.readStructEnd();" << endl << endl;
+
+ indent(out) << "iprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+ indent(out) << "catch(e:Dynamic)" << endl;
+ scope_up(out);
+ indent(out) << "iprot.DecrementRecursionDepth();" << endl;
+ indent(out) << "throw e;" << endl;
+ scope_down(out);
+
+ // check for required fields of primitive type
+ // (which can be checked here but not in the general validate method)
+ out << endl << indent() << "// check for required fields of primitive type, which can't be "
+ "checked in the validate method" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) {
+ out << indent() << "if (!__isset_" << (*f_iter)->get_name() << ") {" << endl << indent()
+ << " throw new TProtocolException(TProtocolException.UNKNOWN, \"Required field '"
+ << (*f_iter)->get_name()
+ << "' was not found in serialized data! Struct: \" + toString());" << endl << indent()
+ << "}" << endl;
+ }
+ }
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+// generates haxe method to perform various checks
+// (e.g. check that all required fields are set)
+void t_haxe_generator::generate_haxe_validator(ostream& out, t_struct* tstruct) {
+ indent(out) << "public function validate() : Void {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "// check for required fields" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ if (type_can_be_null((*f_iter)->get_type())) {
+ indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out)
+ << " throw new TProtocolException(TProtocolException.UNKNOWN, \"Required field '"
+ << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name()
+ << "' because it's a primitive." << endl;
+ }
+ }
+ }
+
+ // check that fields of type enum have valid values
+ out << indent() << "// check that fields of type enum have valid values" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ t_type* type = field->get_type();
+ // if field is an enum, check that its value is valid
+ if (type->is_enum()) {
+ indent(out) << "if (" << generate_isset_check(field) << " && !"
+ << get_cap_name(get_enum_class_name(type)) << ".VALID_VALUES.contains("
+ << field->get_name() << ")){" << endl;
+ indent_up();
+ indent(out) << "throw new TProtocolException(TProtocolException.UNKNOWN, \"The field '"
+ << field->get_name() << "' has been assigned the invalid value \" + "
+ << field->get_name() << ");" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_haxe_struct_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public function write(oprot:TProtocol) : Void {" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl;
+ indent(out) << "oprot.IncrementRecursionDepth();" << endl;
+ indent(out) << "try" << endl;
+ scope_up(out);
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ }
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl;
+ indent_up();
+ }
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ if (null_allowed) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ if (could_be_unset) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ indent(out) << "oprot.writeFieldStop();" << endl;
+ indent(out) << "oprot.writeStructEnd();" << endl;
+
+ indent(out) << "oprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+ indent(out) << "catch(e:Dynamic)" << endl;
+ scope_up(out);
+ indent(out) << "oprot.DecrementRecursionDepth();" << endl;
+ indent(out) << "throw e;" << endl;
+ scope_down(out);
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct,
+ * which is a function result. These fields are only written
+ * if they are set in the Isset array, and only one of them
+ * can be set at a time.
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_haxe_struct_result_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public function write(oprot:TProtocol) : Void {" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "oprot.IncrementRecursionDepth();" << endl;
+ indent(out) << "try" << endl;
+ scope_up(out);
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ out << endl << indent() << "if ";
+ } else {
+ out << " else if ";
+ }
+
+ out << "(this." << generate_isset_check(*f_iter) << ") {" << endl;
+
+ indent_up();
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}";
+ }
+
+ indent(out) << endl;
+ indent(out) << "oprot.writeFieldStop();" << endl;
+ indent(out) << "oprot.writeStructEnd();" << endl;
+
+ indent(out) << "oprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+ indent(out) << "catch(e:Dynamic)" << endl;
+ scope_up(out);
+ indent(out) << "oprot.DecrementRecursionDepth();" << endl;
+ indent(out) << "throw e;" << endl;
+ scope_down(out);
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_haxe_generator::generate_reflection_getters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ (void)type;
+ (void)cap_name;
+ indent(out) << "case " << upcase_string(field_name) << "_FIELD_ID:" << endl;
+ indent_up();
+ indent(out) << "return this." << field_name << ";" << endl;
+ indent_down();
+}
+
+void t_haxe_generator::generate_reflection_setters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ (void)type;
+ (void)cap_name;
+ indent(out) << "case " << upcase_string(field_name) << "_FIELD_ID:" << endl;
+ indent_up();
+ indent(out) << "if (value == null) {" << endl;
+ indent(out) << " unset" << get_cap_name(field_name) << "();" << endl;
+ indent(out) << "} else {" << endl;
+ indent(out) << " this." << field_name << " = value;" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent_down();
+}
+
+void t_haxe_generator::generate_generic_field_getters_setters(std::ostream& out,
+ t_struct* tstruct) {
+
+ std::ostringstream getter_stream;
+ std::ostringstream setter_stream;
+
+ // build up the bodies of both the getter and setter at once
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ indent_up();
+ generate_reflection_setters(setter_stream, type, field_name, cap_name);
+ generate_reflection_getters(getter_stream, type, field_name, cap_name);
+ indent_down();
+ }
+
+ // create the setter
+ indent(out) << "public function setFieldValue(fieldID : Int, value : Dynamic) : Void {" << endl;
+ indent_up();
+
+ if (fields.size() > 0) {
+ indent(out) << "switch (fieldID) {" << endl;
+ out << setter_stream.str();
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // create the getter
+ indent(out) << "public function getFieldValue(fieldID : Int) : Dynamic {" << endl;
+ indent_up();
+
+ if (fields.size() > 0) {
+ indent(out) << "switch (fieldID) {" << endl;
+ out << getter_stream.str();
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+ }
+
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+// Creates a generic isSet method that takes the field number as argument
+void t_haxe_generator::generate_generic_isset_method(std::ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // create the isSet method
+ indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a "
+ "value) and false otherwise" << endl;
+ indent(out) << "public function isSet(fieldID : Int) : Bool {" << endl;
+ indent_up();
+ if (fields.size() > 0) {
+ indent(out) << "switch (fieldID) {" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ indent(out) << "case " << upcase_string(field->get_name()) << "_FIELD_ID:" << endl;
+ indent_up();
+ indent(out) << "return " << generate_isset_check(field) << ";" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a set of property setters/getters for the given struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_property_getters_setters(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ // Simple getter
+ generate_haxe_doc(out, field);
+ indent(out) << "public function get_" << field_name << "() : " << get_cap_name(type_name(type))
+ << " {" << endl;
+ indent_up();
+ indent(out) << "return this." << field_name << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Simple setter
+ generate_haxe_doc(out, field);
+ indent(out) << "public function set_" << field_name << "(" << field_name << ":"
+ << get_cap_name(type_name(type)) << ") : " << get_cap_name(type_name(type)) << " {"
+ << endl;
+ indent_up();
+ indent(out) << "this." << field_name << " = " << field_name << ";" << endl;
+ generate_isset_set(out, field);
+ indent(out) << "return this." << field_name << ";" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Unsetter
+ indent(out) << "public function unset" << cap_name << "() : Void {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "this." << field_name << " = null;" << endl;
+ } else {
+ indent(out) << "this.__isset_" << field_name << " = false;" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // isSet method
+ indent(out) << "// Returns true if field " << field_name
+ << " is set (has been assigned a value) and false otherwise" << endl;
+ indent(out) << "public function is" << get_cap_name("set") << cap_name << "() : Bool {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "return this." << field_name << " != null;" << endl;
+ } else {
+ indent(out) << "return this.__isset_" << field_name << ";" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a toString() method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_haxe_struct_tostring(ostream& out, t_struct* tstruct) {
+ out << indent() << "public "
+ << "function toString() : String {" << endl;
+ indent_up();
+
+ out << indent() << "var ret : String = \"" << tstruct->get_name() << "(\";" << endl;
+ out << indent() << "var first : Bool = true;" << endl << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ }
+
+ t_field* field = (*f_iter);
+
+ if (!first) {
+ indent(out) << "if (!first) ret += \", \";" << endl;
+ }
+ indent(out) << "ret += \"" << (*f_iter)->get_name() << ":\";" << endl;
+ bool can_be_null = type_can_be_null(field->get_type());
+ if (can_be_null) {
+ indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out) << " ret += \"null\";" << endl;
+ indent(out) << "} else {" << endl;
+ indent_up();
+ }
+
+ if (field->get_type()->is_binary()) {
+ indent(out) << " ret += \"BINARY\";" << endl;
+ } else if (field->get_type()->is_enum()) {
+ indent(out) << "var " << field->get_name()
+ << "_name : String = " << get_cap_name(get_enum_class_name(field->get_type()))
+ << ".VALUES_TO_NAMES[this." << (*f_iter)->get_name() << "];" << endl;
+ indent(out) << "if (" << field->get_name() << "_name != null) {" << endl;
+ indent(out) << " ret += " << field->get_name() << "_name;" << endl;
+ indent(out) << " ret += \" (\";" << endl;
+ indent(out) << "}" << endl;
+ indent(out) << "ret += this." << field->get_name() << ";" << endl;
+ indent(out) << "if (" << field->get_name() << "_name != null) {" << endl;
+ indent(out) << " ret += \")\";" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "ret += this." << (*f_iter)->get_name() << ";" << endl;
+ }
+
+ if (can_be_null) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ indent(out) << "first = false;" << endl;
+
+ if (could_be_unset) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ first = false;
+ }
+ out << indent() << "ret += \")\";" << endl << indent() << "return ret;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a static map with meta data to store information such as fieldID to
+ * fieldName mapping
+ *
+ * @param tstruct The struct definition
+ */
+void t_haxe_generator::generate_haxe_meta_data_map(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Static Map with fieldID -> FieldMetaData mappings
+ indent(out) << "inline static var metaDataMap : IntMap = new IntMap();" << endl;
+
+ if (fields.size() > 0) {
+ // Populate map
+ scope_up(out);
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ std::string field_name = field->get_name();
+ indent(out) << "metaDataMap[" << upcase_string(field_name)
+ << "_FIELD_ID] = new FieldMetaData(\"" << field_name << "\", ";
+
+ // Set field requirement type (required, optional, etc.)
+ if (field->get_req() == t_field::T_REQUIRED) {
+ out << "TFieldRequirementType.REQUIRED, ";
+ } else if (field->get_req() == t_field::T_OPTIONAL) {
+ out << "TFieldRequirementType.OPTIONAL, ";
+ } else {
+ out << "TFieldRequirementType.DEFAULT, ";
+ }
+
+ // Create value meta data
+ generate_field_value_meta_data(out, field->get_type());
+ out << ");" << endl;
+ }
+ scope_down(out);
+ }
+}
+
+/**
+ * Returns a string with the haxe representation of the given thrift type
+ * (e.g. for the type struct it returns "TType.STRUCT")
+ */
+std::string t_haxe_generator::get_haxe_type_string(t_type* type) {
+ if (type->is_list()) {
+ return "TType.LIST";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_typedef()) {
+ return get_haxe_type_string(((t_typedef*)type)->get_type());
+ } else if (type->is_base_type()) {
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "TType.VOID";
+ break;
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ break;
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ break;
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ break;
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ break;
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ break;
+ default:
+ throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_haxe_generator::get_haxe_type_string!");
+ break; // This should never happen!
+ }
+ } else {
+ throw std::runtime_error(
+ "Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_haxe_generator::get_haxe_type_string!"); // This should never happen!
+ }
+}
+
+void t_haxe_generator::generate_field_value_meta_data(std::ostream& out, t_type* type) {
+ out << endl;
+ indent_up();
+ indent_up();
+ if (type->is_struct()) {
+ indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type);
+ } else if (type->is_container()) {
+ if (type->is_list()) {
+ indent(out) << "new ListMetaData(TType.LIST, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else if (type->is_set()) {
+ indent(out) << "new SetMetaData(TType.SET, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else { // map
+ indent(out) << "new MapMetaData(TType.MAP, ";
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+ generate_field_value_meta_data(out, key_type);
+ out << ", ";
+ generate_field_value_meta_data(out, val_type);
+ }
+ } else {
+ indent(out) << "new FieldValueMetaData(" << get_haxe_type_string(type);
+ }
+ out << ")";
+ indent_down();
+ indent_down();
+}
+
+/**
+ * Generates a thrift service. In C++, this comprises an entirely separate
+ * header and source file. The header file defines the methods and includes
+ * the data types defined in the main header file, and the implementation
+ * file contains implementations of the basic printer and default interfaces.
+ *
+ * @param tservice The service definition
+ */
+void t_haxe_generator::generate_service(t_service* tservice) {
+ // Make interface file
+ string f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + ".hx";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << haxe_package() << ";" << endl;
+
+ f_service_ << endl << haxe_type_imports() << haxe_thrift_imports()
+ << haxe_thrift_gen_imports(tservice);
+
+ if (tservice->get_extends() != NULL) {
+ t_type* parent = tservice->get_extends();
+ string parent_namespace = parent->get_program()->get_namespace("haxe");
+ if (!parent_namespace.empty() && parent_namespace != package_name_) {
+ f_service_ << "import " << type_name(parent) << ";" << endl;
+ }
+ }
+
+ f_service_ << endl;
+
+ generate_service_interface(tservice);
+
+ f_service_.close();
+
+ // Now make the implementation/client file
+ f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + "Impl.hx";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << haxe_package() << ";" << endl << endl << haxe_type_imports()
+ << haxe_thrift_imports() << haxe_thrift_gen_imports(tservice) << endl;
+
+ if (tservice->get_extends() != NULL) {
+ t_type* parent = tservice->get_extends();
+ string parent_namespace = parent->get_program()->get_namespace("haxe");
+ if (!parent_namespace.empty() && parent_namespace != package_name_) {
+ f_service_ << "import " << type_name(parent) << "Impl;" << endl;
+ }
+ }
+
+ f_service_ << endl;
+
+ generate_service_client(tservice);
+
+ f_service_.close();
+
+ // Now make the helper class files
+ generate_service_helpers(tservice);
+
+ // Now make the processor/server file
+ f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + "Processor.hx";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << haxe_package() << ";" << endl << endl << haxe_type_imports()
+ << haxe_thrift_imports() << haxe_thrift_gen_imports(tservice) << endl;
+
+ if (!package_name_.empty()) {
+ f_service_ << "import " << package_name_ << ".*;" << endl;
+ f_service_ << "import " << package_name_ << "." << get_cap_name(service_name_).c_str()
+ << "Impl;" << endl;
+ f_service_ << endl;
+ }
+
+ generate_service_server(tservice);
+
+ f_service_.close();
+}
+
+/**
+ * Generates the code snippet for the onSuccess callbacks
+ *
+ * @param tfunction The service function to generate code for.
+ */
+string t_haxe_generator::generate_service_method_onsuccess(t_function* tfunction,
+ bool as_type,
+ bool omit_name) {
+ if (tfunction->is_oneway()) {
+ return "";
+ }
+
+ string name = "";
+ if (!omit_name) {
+ name = "onSuccess";
+ if (as_type) {
+ name += " : ";
+ }
+ }
+
+ if (tfunction->get_returntype()->is_void()) {
+ if (as_type) {
+ return name + "Void->Void = null";
+ } else {
+ return name + "() : Void";
+ }
+ }
+
+ if (as_type) {
+ return name + type_name(tfunction->get_returntype()) + "->Void = null";
+ } else {
+ return name + "( retval : " + type_name(tfunction->get_returntype()) + ")";
+ }
+}
+
+/**
+ * Generates a service method header
+ *
+ * @param tfunction The service function to generate code for.
+ */
+void t_haxe_generator::generate_service_method_signature(t_function* tfunction, bool is_interface) {
+ if (callbacks_) {
+ generate_service_method_signature_callback(tfunction, is_interface);
+ } else {
+ generate_service_method_signature_normal(tfunction, is_interface);
+ }
+}
+
+/**
+ * Generates a service method header in "normal" style
+ *
+ * @param tfunction The service function to generate code for.
+ */
+void t_haxe_generator::generate_service_method_signature_normal(t_function* tfunction,
+ bool is_interface) {
+ if (is_interface) {
+ indent(f_service_) << function_signature_normal(tfunction) << ";" << endl << endl;
+ } else {
+ indent(f_service_) << "public " << function_signature_normal(tfunction) << " {" << endl;
+ }
+}
+
+/**
+ * Generates a service method header in "callback" style
+ *
+ * @param tfunction The service function to generate code for.
+ */
+void t_haxe_generator::generate_service_method_signature_callback(t_function* tfunction,
+ bool is_interface) {
+ if (!tfunction->is_oneway()) {
+ std::string on_success_impl = generate_service_method_onsuccess(tfunction, false, false);
+ indent(f_service_) << "// function onError(Dynamic) : Void;" << endl;
+ indent(f_service_) << "// function " << on_success_impl.c_str() << ";" << endl;
+ }
+
+ if (is_interface) {
+ indent(f_service_) << function_signature_callback(tfunction) << ";" << endl << endl;
+ } else {
+ indent(f_service_) << "public " << function_signature_callback(tfunction) << " {" << endl;
+ }
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_haxe_generator::generate_service_interface(t_service* tservice) {
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends_iface = " extends " + tservice->get_extends()->get_name();
+ }
+
+ generate_haxe_doc(f_service_, tservice);
+ // generate_rtti_decoration(f_service_); - not yet, because of
+ // https://github.com/HaxeFoundation/haxe/issues/3626
+ generate_macro_decoration(f_service_);
+ f_service_ << indent() << "interface " << get_cap_name(service_name_) << extends_iface << " {"
+ << endl << endl;
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_haxe_doc(f_service_, *f_iter);
+ generate_service_method_signature(*f_iter, true);
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates structs for all the service args and return types
+ *
+ * @param tservice The service
+ */
+void t_haxe_generator::generate_service_helpers(t_service* tservice) {
+ f_service_ << endl << endl;
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_haxe_struct(ts, false);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_haxe_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = get_cap_name(tservice->get_extends()->get_name());
+ extends_client = " extends " + extends + "Impl";
+ }
+
+ generate_rtti_decoration(f_service_);
+ // build macro is inherited from interface
+ indent(f_service_) << "class " << get_cap_name(service_name_) << "Impl" << extends_client
+ << " implements " << get_cap_name(service_name_) << " {" << endl << endl;
+ indent_up();
+
+ indent(f_service_) << "public function new( iprot : TProtocol, oprot : TProtocol = null)" << endl;
+ scope_up(f_service_);
+ if (extends.empty()) {
+ f_service_ << indent() << "iprot_ = iprot;" << endl;
+ f_service_ << indent() << "if (oprot == null) {" << endl;
+ indent_up();
+ f_service_ << indent() << "oprot_ = iprot;" << endl;
+ indent_down();
+ f_service_ << indent() << "} else {" << endl;
+ indent_up();
+ f_service_ << indent() << "oprot_ = oprot;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ } else {
+ f_service_ << indent() << "super(iprot, oprot);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "private var iprot_ : TProtocol;" << endl << indent()
+ << "private var oprot_ : TProtocol;" << endl << indent()
+ << "private var seqid_ : Int;" << endl << endl;
+
+ indent(f_service_) << "public function getInputProtocol() : TProtocol" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return this.iprot_;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << "public function getOutputProtocol() : TProtocol" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return this.oprot_;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ generate_service_method_signature(*f_iter, false);
+
+ indent_up();
+
+ // Get the struct of function call params
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ string argsname = get_cap_name((*f_iter)->get_name() + "_args");
+ vector<t_field*>::const_iterator fld_iter;
+ const vector<t_field*>& fields = arg_struct->get_members();
+
+ // Serialize the request
+ string calltype = (*f_iter)->is_oneway() ? "ONEWAY" : "CALL";
+ f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname
+ << "\", TMessageType." << calltype << ", seqid_));" << endl << indent()
+ << "var args : " << argsname << " = new " << argsname << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = "
+ << (*fld_iter)->get_name() << ";" << endl;
+ }
+
+ f_service_ << indent() << "args.write(oprot_);" << endl << indent()
+ << "oprot_.writeMessageEnd();" << endl;
+
+ if (!((*f_iter)->is_oneway() || (*f_iter)->get_returntype()->is_void())) {
+ f_service_ << indent() << "var retval : " << type_name((*f_iter)->get_returntype()) << ";"
+ << endl;
+ }
+
+ if ((*f_iter)->is_oneway()) {
+ f_service_ << indent() << "oprot_.getTransport().flush();" << endl;
+ } else {
+ indent(f_service_) << "oprot_.getTransport().flush(function(error:Dynamic) : Void {" << endl;
+ indent_up();
+ if (callbacks_) {
+ indent(f_service_) << "try {" << endl;
+ indent_up();
+ }
+ string resultname = get_cap_name((*f_iter)->get_name() + "_result");
+ indent(f_service_) << "if (error != null) {" << endl;
+ indent_up();
+ if (callbacks_) {
+ indent(f_service_) << "if (onError != null) onError(error);" << endl;
+ indent(f_service_) << "return;" << endl;
+ } else {
+ indent(f_service_) << "throw error;" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ indent(f_service_) << "var msg : TMessage = iprot_.readMessageBegin();" << endl;
+ indent(f_service_) << "if (msg.type == TMessageType.EXCEPTION) {" << endl;
+ indent_up();
+ indent(f_service_) << "var x = TApplicationException.read(iprot_);" << endl;
+ indent(f_service_) << "iprot_.readMessageEnd();" << endl;
+ if (callbacks_) {
+ indent(f_service_) << "if (onError != null) onError(x);" << endl;
+ indent(f_service_) << "return;" << endl;
+ } else {
+ indent(f_service_) << "throw x;" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ indent(f_service_) << "var result : " << resultname << " = new " << resultname << "();"
+ << endl;
+ indent(f_service_) << "result.read(iprot_);" << endl;
+ indent(f_service_) << "iprot_.readMessageEnd();" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "if (result." << generate_isset_check("success") << ") {" << endl;
+ indent_up();
+ if (callbacks_) {
+ indent(f_service_) << "if (onSuccess != null) onSuccess(result.success);" << endl;
+ indent(f_service_) << "return;" << endl;
+ } else {
+ indent(f_service_) << "retval = result.success;" << endl;
+ indent(f_service_) << "return;" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent(f_service_) << "if (result." << (*x_iter)->get_name() << " != null) {" << endl;
+ indent_up();
+ if (callbacks_) {
+ indent(f_service_) << "if (onError != null) onError(result." << (*x_iter)->get_name()
+ << ");" << endl;
+ indent(f_service_) << "return;" << endl;
+ } else {
+ indent(f_service_) << "throw result." << (*x_iter)->get_name() << ";" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ }
+
+ // If you get here it's an exception, unless a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ if (callbacks_) {
+ indent(f_service_) << "if (onSuccess != null) onSuccess();" << endl;
+ }
+ indent(f_service_) << "return;" << endl;
+ } else {
+ if (callbacks_) {
+ indent(f_service_) << "if (onError != null)" << endl;
+ indent_up();
+ indent(f_service_)
+ << "onError( new TApplicationException(TApplicationException.MISSING_RESULT," << endl;
+ indent(f_service_) << " \"" << (*f_iter)->get_name()
+ << " failed: unknown result\"));" << endl;
+ indent_down();
+ } else {
+ indent(f_service_)
+ << "throw new TApplicationException(TApplicationException.MISSING_RESULT," << endl;
+ indent(f_service_) << " \"" << (*f_iter)->get_name()
+ << " failed: unknown result\");" << endl;
+ }
+ }
+
+ if (callbacks_) {
+ indent_down();
+ indent(f_service_) << "} catch( e : TException) {" << endl;
+ indent_up();
+ indent(f_service_) << "if (onError != null) onError(e);" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ }
+
+ indent_down();
+ indent(f_service_) << "});" << endl;
+ }
+
+ if (!((*f_iter)->is_oneway() || (*f_iter)->get_returntype()->is_void())) {
+ f_service_ << indent() << "return retval;" << endl;
+ }
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_haxe_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Extends stuff
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = get_cap_name(type_name(tservice->get_extends()));
+ extends_processor = " extends " + extends + "Processor";
+ }
+
+ // Generate the header portion
+ generate_rtti_decoration(f_service_);
+ generate_macro_decoration(f_service_);
+ indent(f_service_) << "class " << get_cap_name(service_name_) << "Processor" << extends_processor
+ << " implements TProcessor {" << endl << endl;
+ indent_up();
+
+ f_service_ << indent() << "private var " << get_cap_name(service_name_)
+ << "_iface_ : " << get_cap_name(service_name_) << ";" << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent()
+ << "private var PROCESS_MAP = new StringMap< Int->TProtocol->TProtocol->Void >();"
+ << endl;
+ }
+
+ f_service_ << endl;
+
+ indent(f_service_) << "public function new( iface : " << get_cap_name(service_name_) << ")"
+ << endl;
+ scope_up(f_service_);
+ if (!extends.empty()) {
+ f_service_ << indent() << "super(iface);" << endl;
+ }
+ f_service_ << indent() << get_cap_name(service_name_) << "_iface_ = iface;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "PROCESS_MAP.set(\"" << (*f_iter)->get_name() << "\", "
+ << (*f_iter)->get_name() << "());" << endl;
+ }
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the server implementation
+ string override = "";
+ if (tservice->get_extends() != NULL) {
+ override = "override ";
+ }
+ indent(f_service_) << override
+ << "public function process( iprot : TProtocol, oprot : TProtocol) : Bool"
+ << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "var msg : TMessage = iprot.readMessageBegin();" << endl;
+
+ // TODO(mcslee): validate message, was the seqid etc. legit?
+ // AS- If all method is oneway:
+ // do you have an oprot?
+ // do you you need nullcheck?
+ f_service_
+ << indent() << "var fn = PROCESS_MAP.get(msg.name);" << endl << indent()
+ << "if (fn == null) {" << endl << indent() << " TProtocolUtil.skip(iprot, TType.STRUCT);"
+ << endl << indent() << " iprot.readMessageEnd();" << endl << indent()
+ << " var x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, \"Invalid "
+ "method name: '\"+msg.name+\"'\");" << endl << indent()
+ << " oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));"
+ << endl << indent() << " x.write(oprot);" << endl << indent() << " oprot.writeMessageEnd();"
+ << endl << indent() << " oprot.getTransport().flush();" << endl << indent()
+ << " return true;" << endl << indent() << "}" << endl << indent()
+ << "fn( msg.seqid, iprot, oprot);" << endl;
+
+ f_service_ << indent() << "return true;" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_haxe_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ string resultname = get_cap_name(tfunction->get_name() + "_result");
+ t_struct result(program_, resultname);
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_haxe_struct(&result, false, true);
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_haxe_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open class
+ indent(f_service_) << "private function " << tfunction->get_name()
+ << "() : Int->TProtocol->TProtocol->Void {" << endl;
+ indent_up();
+
+ // Open function
+ indent(f_service_) << "return function( seqid : Int, iprot : TProtocol, oprot : TProtocol) : Void"
+ << endl;
+ scope_up(f_service_);
+
+ string argsname = get_cap_name(tfunction->get_name() + "_args");
+ string resultname = get_cap_name(tfunction->get_name() + "_result");
+
+ f_service_ << indent() << "var args : " << argsname << " = new " << argsname << "();" << endl
+ << indent() << "args.read(iprot);" << endl << indent() << "iprot.readMessageEnd();"
+ << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "var result : " << resultname << " = new " << resultname << "();"
+ << endl;
+ }
+
+ // Try block for any function to catch (defined or undefined) exceptions
+ f_service_ << indent() << "try {" << endl;
+ indent_up();
+
+ if (callbacks_) {
+ // callback function style onError/onSuccess
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ f_service_ << get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+
+ if (tfunction->is_oneway()) {
+ f_service_ << ");" << endl;
+ } else {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ string on_success = generate_service_method_onsuccess(tfunction, false, true);
+ indent_up();
+ f_service_ << endl;
+ indent(f_service_) << "null, // errors are thrown by the handler" << endl;
+ if (tfunction->get_returntype()->is_void()) {
+ indent(f_service_) << "null); // no retval" << endl;
+ } else {
+ indent(f_service_) << "function" << on_success.c_str() << " {" << endl;
+ if (!tfunction->get_returntype()->is_void()) {
+ indent_up();
+ indent(f_service_) << "result.success = retval;" << endl;
+ indent_down();
+ }
+ indent(f_service_) << "});" << endl;
+ }
+ indent_down();
+ }
+
+ } else {
+ // normal function():result style
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (!(tfunction->is_oneway() || tfunction->get_returntype()->is_void())) {
+ f_service_ << "result.success = ";
+ }
+ f_service_ << get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+ }
+
+ indent_down();
+ f_service_ << indent() << "}";
+ if (!tfunction->is_oneway()) {
+ // catch exceptions defined in the IDL
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << " catch (" << (*x_iter)->get_name() << ":"
+ << get_cap_name(type_name((*x_iter)->get_type(), false, false)) << ") {" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ f_service_ << indent() << "result." << (*x_iter)->get_name() << " = "
+ << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ f_service_ << indent() << "}";
+ } else {
+ f_service_ << "}";
+ }
+ }
+ }
+
+ // always catch all exceptions to prevent from service denial
+ f_service_ << " catch (th : Dynamic) {" << endl;
+ indent_up();
+ indent(f_service_) << "trace(\"Internal error processing " << tfunction->get_name() << "\", th);"
+ << endl;
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << "var x = new TApplicationException(TApplicationException.INTERNAL_ERROR, "
+ "\"Internal error processing " << tfunction->get_name() << "\");" << endl;
+ indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.EXCEPTION, seqid));" << endl;
+ indent(f_service_) << "x.write(oprot);" << endl;
+ indent(f_service_) << "oprot.writeMessageEnd();" << endl;
+ indent(f_service_) << "oprot.getTransport().flush();" << endl;
+ }
+ indent(f_service_) << "return;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "return;" << endl;
+ scope_down(f_service_);
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+ return;
+ }
+
+ f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);"
+ << endl << indent() << "oprot.writeMessageEnd();" << endl << indent()
+ << "oprot.getTransport().flush();" << endl;
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ *
+ * @param tfield The field
+ * @param prefix The variable name or container for this field
+ */
+void t_haxe_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent(out) << name << " = iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "readBinary();";
+ } else {
+ out << "readString();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble();";
+ break;
+ default:
+ throw "compiler error: no Haxe name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32();";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, invokes read()
+ */
+void t_haxe_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+ out << indent() << prefix << " = new " << get_cap_name(type_name(tstruct)) << "();" << endl
+ << indent() << prefix << ".read(iprot);" << endl;
+}
+
+/**
+ * Deserializes a container by reading its size and then iterating
+ */
+void t_haxe_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "var " << obj << " = iprot.readMapBegin();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "var " << obj << " = iprot.readSetBegin();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "var " << obj << " = iprot.readListBegin();" << endl;
+ }
+
+ indent(out) << prefix << " = new " << type_name(ttype, false, true)
+ // size the collection correctly
+ << "("
+ << ");" << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for( " << i << " in 0 ... " << obj << ".size)" << endl;
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot.readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot.readListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_haxe_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey) << endl;
+ indent(out) << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << ".set( " << key << ", " << val << ");" << endl;
+}
+
+/**
+ * Deserializes a set element
+ */
+void t_haxe_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Deserializes a list element
+ */
+void t_haxe_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_haxe_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name());
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + tfield->get_name();
+ indent(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ");";
+ } else {
+ out << "writeString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no Haxe name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_haxe_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ out << indent() << prefix << ".write(oprot);" << endl;
+}
+
+/**
+ * Serializes a container by writing its size then the elements.
+ *
+ * @param ttype The type of container
+ * @param prefix String prefix for fields
+ */
+void t_haxe_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ string iter = tmp("_key");
+ string counter = tmp("_sizeCounter");
+ indent(out) << "var " << counter << " : Int = 0;" << endl;
+ indent(out) << "for( " << iter << " in " << prefix << ") {" << endl;
+ indent(out) << " " << counter << +"++;" << endl;
+ indent(out) << "}" << endl;
+
+ indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << counter << "));"
+ << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", " << prefix << ".size));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListBegin(new TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));"
+ << endl;
+ }
+
+ string iter = tmp("elem");
+ if (ttype->is_map()) {
+ indent(out) << "for( " << iter << " in " << prefix << ".keys())" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "for( " << iter << " in " << prefix << ".toArray())" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "for( " << iter << " in " << prefix << ")" << endl;
+ }
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter, prefix);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ }
+
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ */
+void t_haxe_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string iter,
+ string map) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "");
+ t_field vfield(tmap->get_val_type(), map + ".get(" + iter + ")");
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_haxe_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_haxe_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Returns a haxe type name
+ *
+ * @param ttype The type
+ * @param container Is the type going inside a container?
+ * @return haxe type name, i.e. HashMap<Key,Value>
+ */
+string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_init) {
+ (void)in_init;
+
+ // typedefs are just resolved to their real type
+ ttype = get_true_type(ttype);
+ string prefix;
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype, in_container);
+ }
+
+ if (ttype->is_enum()) {
+ return "Int";
+ }
+
+ if (ttype->is_map()) {
+ t_type* tkey = get_true_type(((t_map*)ttype)->get_key_type());
+ t_type* tval = get_true_type(((t_map*)ttype)->get_val_type());
+ if (tkey->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ if (!(tkey->is_binary())) {
+ return "StringMap< " + type_name(tval) + ">";
+ }
+ break; // default to ObjectMap<>
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ return "IntMap< " + type_name(tval) + ">";
+ case t_base_type::TYPE_I64:
+ return "Int64Map< " + type_name(tval) + ">";
+ default:
+ break; // default to ObjectMap<>
+ }
+ }
+ if (tkey->is_enum()) {
+ return "IntMap< " + type_name(tval) + ">";
+ }
+ return "ObjectMap< " + type_name(tkey) + ", " + type_name(tval) + ">";
+ }
+
+ if (ttype->is_set()) {
+ t_type* tkey = get_true_type(((t_set*)ttype)->get_elem_type());
+ if (tkey->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ if (!(tkey->is_binary())) {
+ return "StringSet";
+ }
+ break; // default to ObjectSet
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ return "IntSet";
+ case t_base_type::TYPE_I64:
+ return "Int64Set";
+ default:
+ break; // default to ObjectSet
+ }
+ }
+ if (tkey->is_enum()) {
+ return "IntSet";
+ }
+ return "ObjectSet< " + type_name(tkey) + ">";
+ }
+
+ if (ttype->is_list()) {
+ t_type* telm = ((t_list*)ttype)->get_elem_type();
+ return "List< " + type_name(telm) + ">";
+ }
+
+ // Check for namespacing
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("haxe");
+ if (!package.empty()) {
+ return package + "." + ttype->get_name();
+ }
+ }
+
+ return ttype->get_name();
+}
+
+/**
+ * Returns the haxe type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ * @param container Is it going in a haxe container?
+ */
+string t_haxe_generator::base_type_name(t_base_type* type, bool in_container) {
+ (void)in_container;
+ t_base_type::t_base tbase = type->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "Void";
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ return "haxe.io.Bytes";
+ } else {
+ return "String";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "Bool";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ return "haxe.Int32";
+ case t_base_type::TYPE_I64:
+ return "haxe.Int64";
+ case t_base_type::TYPE_DOUBLE:
+ return "Float";
+ default:
+ throw "compiler error: no Haxe name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_haxe_generator::declare_field(t_field* tfield, bool init) {
+ // TODO(mcslee): do we ever need to initialize the field?
+ string result = "var " + tfield->get_name() + " : " + type_name(tfield->get_type());
+ if (init) {
+ t_type* ttype = get_true_type(tfield->get_type());
+ if (ttype->is_base_type() && tfield->get_value() != NULL) {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
+ } else if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+
+ } else if (ttype->is_enum()) {
+ result += " = 0";
+ } else if (ttype->is_container()) {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ } else {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ }
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_haxe_generator::function_signature_callback(t_function* tfunction) {
+ std::string on_error_success = "onError : Dynamic->Void = null, "
+ + generate_service_method_onsuccess(tfunction, true, false);
+
+ std::string arguments = argument_list(tfunction->get_arglist());
+ if (!tfunction->is_oneway()) {
+ if (arguments != "") {
+ arguments += ", ";
+ }
+ arguments += on_error_success; //"onError : Function, onSuccess : Function";
+ }
+
+ std::string result = "function " + tfunction->get_name() + "(" + arguments + ") : Void";
+ return result;
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_haxe_generator::function_signature_normal(t_function* tfunction) {
+ std::string arguments = argument_list(tfunction->get_arglist());
+
+ std::string resulttype;
+ if (tfunction->is_oneway() || tfunction->get_returntype()->is_void()) {
+ resulttype = "Void";
+ } else {
+ resulttype = type_name(tfunction->get_returntype());
+ }
+
+ std::string result = "function " + tfunction->get_name() + "(" + arguments + ") : " + resulttype;
+ return result;
+}
+
+/**
+ * Renders a comma separated field list, with type names
+ */
+string t_haxe_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += (*f_iter)->get_name() + " : " + type_name((*f_iter)->get_type());
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_haxe_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Haxe class names must start with uppercase letter, but Haxe namespaces must not.
+ */
+std::string t_haxe_generator::get_cap_name(std::string name) {
+ if (name.length() == 0) {
+ return name;
+ }
+
+ // test.for.Generic< data.Type, or.the.Like> and handle it recursively
+ size_t generic_first = name.find('<');
+ size_t generic_last = name.rfind('>');
+ if ((generic_first != std::string::npos) && (generic_last != std::string::npos)) {
+ string outer_type = name.substr(0, generic_first);
+ string inner_types = name.substr(generic_first + 1, generic_last - generic_first - 1);
+
+ string new_inner = "";
+ size_t comma_start = 0;
+ while (comma_start < inner_types.length()) {
+ size_t comma_pos = comma_start;
+ int nested = 0;
+
+ while (comma_pos < inner_types.length()) {
+ bool found = false;
+ switch (inner_types[comma_pos]) {
+ case '<':
+ ++nested;
+ break;
+ case '>':
+ --nested;
+ break;
+ case ',':
+ found = (nested == 0);
+ break;
+ }
+ if (found) {
+ break;
+ }
+ ++comma_pos;
+ }
+
+ if (new_inner.length() > 0) {
+ new_inner += ",";
+ }
+
+ string inner = inner_types.substr(comma_start, comma_pos - comma_start);
+ new_inner += get_cap_name(inner);
+ comma_start = ++comma_pos;
+ }
+
+ return get_cap_name(outer_type) + "<" + new_inner + ">";
+ }
+
+ // package name
+ size_t index = name.find_first_not_of(" \n\r\t");
+ if (index < name.length()) {
+ name[index] = tolower(name[index]);
+ index = name.find('.');
+ while (index != std::string::npos) {
+ if (++index < name.length()) {
+ name[index] = tolower(name[index]);
+ }
+ index = name.find('.', index);
+ }
+ }
+
+ // class name
+ index = name.rfind('.');
+ if (index != std::string::npos) {
+ ++index;
+ } else {
+ index = name.find_first_not_of(" \n\r\t");
+ }
+
+ if (index < name.length()) {
+ name[index] = toupper(name[index]);
+ }
+
+ return name;
+}
+
+string t_haxe_generator::constant_name(string name) {
+ string constant_name;
+
+ bool is_first = true;
+ bool was_previous_char_upper = false;
+ for (char character : name) {
+ bool is_upper = isupper(character);
+
+ if (is_upper && !is_first && !was_previous_char_upper) {
+ constant_name += '_';
+ }
+ constant_name += toupper(character);
+
+ is_first = false;
+ was_previous_char_upper = is_upper;
+ }
+
+ return constant_name;
+}
+
+/**
+ * Enables RTTI for a class or interface
+ */
+void t_haxe_generator::generate_rtti_decoration(ostream& out) {
+ if (rtti_) {
+ out << "@:rtti" << endl;
+ }
+}
+
+/**
+ * Adds build macros to a class or interface
+ */
+void t_haxe_generator::generate_macro_decoration(ostream& out) {
+ if (!buildmacro_.empty()) {
+ out << "#if ! macro" << endl;
+ out << "@:build( " << buildmacro_ << ")" << endl; // current class/interface
+ out << "@:autoBuild( " << buildmacro_ << ")" << endl; // inherited classes/interfaces
+ out << "#end" << endl;
+ }
+}
+
+/**
+ * Emits a haxeDoc comment if the provided object has a doc in Thrift
+ */
+void t_haxe_generator::generate_haxe_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_docstring_comment(out, "/**\n", " * ", tdoc->get_doc(), " */\n");
+ }
+}
+
+/**
+ * Emits a haxeDoc comment if the provided function object has a doc in Thrift
+ */
+void t_haxe_generator::generate_haxe_doc(ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc()) {
+ stringstream ss;
+ ss << tfunction->get_doc();
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ss << "\n@param " << p->get_name();
+ if (p->has_doc()) {
+ ss << " " << p->get_doc();
+ }
+ }
+ generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n");
+ }
+}
+
+std::string t_haxe_generator::generate_isset_check(t_field* field) {
+ return generate_isset_check(field->get_name());
+}
+
+std::string t_haxe_generator::generate_isset_check(std::string field_name) {
+ return "is" + get_cap_name("set") + get_cap_name(field_name) + "()";
+}
+
+void t_haxe_generator::generate_isset_set(ostream& out, t_field* field) {
+ if (!type_can_be_null(field->get_type())) {
+ indent(out) << "this.__isset_" << field->get_name() << " = true;" << endl;
+ }
+}
+
+std::string t_haxe_generator::get_enum_class_name(t_type* type) {
+ string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL /*&& program != program_*/) {
+ package = program->get_namespace("haxe") + ".";
+ }
+ return package + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ haxe,
+ "Haxe",
+ " callbacks Use onError()/onSuccess() callbacks for service methods (like AS3)\n"
+ " rtti Enable @:rtti for generated classes and interfaces\n"
+ " buildmacro=my.macros.Class.method(args)\n"
+ " Add @:build macro calls to generated classes and interfaces\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc
new file mode 100644
index 000000000..a59dee5d2
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc
@@ -0,0 +1,1717 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/version.h"
+
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Haskell code generator.
+ *
+ */
+class t_hs_generator : public t_oop_generator {
+public:
+ t_hs_generator(t_program* program,
+ const map<string, string>& parsed_options,
+ const string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option hs:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-hs";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ string render_const_value(t_type* type, t_const_value* value);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_hs_struct(t_struct* tstruct, bool is_exception);
+
+ void generate_hs_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool helper = false);
+
+ void generate_hs_struct_reader(ostream& out, t_struct* tstruct);
+
+ void generate_hs_struct_writer(ostream& out, t_struct* tstruct);
+
+ void generate_hs_struct_arbitrary(ostream& out, t_struct* tstruct);
+
+ void generate_hs_function_helpers(t_function* tfunction);
+
+ void generate_hs_typemap(ostream& out, t_struct* tstruct);
+
+ void generate_hs_default(ostream& out, t_struct* tstruct);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(ostream& out, t_field* tfield, string prefix);
+
+ void generate_deserialize_struct(ostream& out, t_struct* tstruct, string name = "");
+
+ void generate_deserialize_container(ostream& out, t_type* ttype, string arg = "");
+
+ void generate_deserialize_set_element(ostream& out, t_set* tset);
+
+ void generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix = "");
+
+ void generate_deserialize_type(ostream& out, t_type* type, string arg = "");
+
+ void generate_serialize_type(ostream& out, t_type* type, string name = "");
+
+ void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix = "");
+
+ void generate_serialize_container(ostream& out, t_type* ttype, string prefix = "");
+
+ void generate_serialize_map_element(ostream& out, t_map* tmap, string kiter, string viter);
+
+ void generate_serialize_set_element(ostream& out, t_set* tmap, string iter);
+
+ void generate_serialize_list_element(ostream& out, t_list* tlist, string iter);
+
+ /**
+ * Helper rendering functions
+ */
+
+ string hs_autogen_comment();
+ string hs_language_pragma();
+ string hs_imports();
+
+ string type_name(t_type* ttype, string function_prefix = "");
+
+ string field_name(string tname, string fname);
+
+ string function_type(t_function* tfunc,
+ bool options = false,
+ bool io = false,
+ bool method = false);
+
+ string type_to_enum(t_type* ttype);
+
+ string type_to_default(t_type* ttype);
+
+ string render_hs_type(t_type* type, bool needs_parens);
+
+ string type_to_constructor(t_type* ttype);
+
+ string render_hs_type_for_function_name(t_type* type);
+
+private:
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_consts_;
+ ofstream_with_content_based_conditional_update f_service_;
+ ofstream_with_content_based_conditional_update f_iface_;
+ ofstream_with_content_based_conditional_update f_client_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_hs_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ // Make output file
+ string pname = capitalize(program_name_);
+ string f_types_name = get_out_dir() + pname + "_Types.hs";
+ f_types_.open(f_types_name.c_str());
+
+ string f_consts_name = get_out_dir() + pname + "_Consts.hs";
+ f_consts_.open(f_consts_name.c_str());
+
+ // Print header
+ f_types_ << hs_language_pragma() << endl;
+ f_types_ << hs_autogen_comment() << endl;
+ f_types_ << "module " << pname << "_Types where" << endl;
+ f_types_ << hs_imports() << endl;
+
+ f_consts_ << hs_language_pragma() << endl;
+ f_consts_ << hs_autogen_comment() << endl;
+ f_consts_ << "module " << pname << "_Consts where" << endl;
+ f_consts_ << hs_imports() << endl;
+ f_consts_ << "import " << pname << "_Types" << endl;
+}
+
+string t_hs_generator::hs_language_pragma() {
+ return string(
+ "{-# LANGUAGE DeriveDataTypeable #-}\n"
+ "{-# LANGUAGE DeriveGeneric #-}\n"
+ "{-# LANGUAGE OverloadedStrings #-}\n"
+ "{-# OPTIONS_GHC -fno-warn-missing-fields #-}\n"
+ "{-# OPTIONS_GHC -fno-warn-missing-signatures #-}\n"
+ "{-# OPTIONS_GHC -fno-warn-name-shadowing #-}\n"
+ "{-# OPTIONS_GHC -fno-warn-unused-imports #-}\n"
+ "{-# OPTIONS_GHC -fno-warn-unused-matches #-}\n");
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_hs_generator::hs_autogen_comment() {
+ return string("-----------------------------------------------------------------\n")
+ + "-- Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ") --\n"
+ + "-- --\n"
+ + "-- DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING --\n"
+ + "-----------------------------------------------------------------\n";
+}
+
+/**
+ * Prints standard thrift imports
+ */
+string t_hs_generator::hs_imports() {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = string(
+ "import Prelude (($), (.), (>>=), (==), (++))\n"
+ "import qualified Prelude as P\n"
+ "import qualified Control.Exception as X\n"
+ "import qualified Control.Monad as M ( liftM, ap, when )\n"
+ "import Data.Functor ( (<$>) )\n"
+ "import qualified Data.ByteString.Lazy as LBS\n"
+ "import qualified Data.Hashable as H\n"
+ "import qualified Data.Int as I\n"
+ "import qualified Data.Maybe as M (catMaybes)\n"
+ "import qualified Data.Text.Lazy.Encoding as E ( decodeUtf8, encodeUtf8 )\n"
+ "import qualified Data.Text.Lazy as LT\n"
+ "import qualified GHC.Generics as G (Generic)\n"
+ "import qualified Data.Typeable as TY ( Typeable )\n"
+ "import qualified Data.HashMap.Strict as Map\n"
+ "import qualified Data.HashSet as Set\n"
+ "import qualified Data.Vector as Vector\n"
+ "import qualified Test.QuickCheck.Arbitrary as QC ( Arbitrary(..) )\n"
+ "import qualified Test.QuickCheck as QC ( elements )\n"
+ "\n"
+ "import qualified Thrift as T\n"
+ "import qualified Thrift.Types as T\n"
+ "import qualified Thrift.Arbitraries as T\n"
+ "\n");
+
+ for (auto include : includes)
+ result += "import qualified " + capitalize(include->get_name()) + "_Types\n";
+
+ if (includes.size() > 0)
+ result += "\n";
+
+ return result;
+}
+
+/**
+ * Closes the type files
+ */
+void t_hs_generator::close_generator() {
+ // Close types file
+ f_types_.close();
+ f_consts_.close();
+}
+
+/**
+ * Generates a typedef. Ez.
+ *
+ * @param ttypedef The type definition
+ */
+void t_hs_generator::generate_typedef(t_typedef* ttypedef) {
+ string tname = capitalize(ttypedef->get_symbolic());
+ string tdef = render_hs_type(ttypedef->get_type(), false);
+ indent(f_types_) << "type " << tname << " = " << tdef << endl;
+ f_types_ << endl;
+}
+
+/**
+ * Generates code for an enumerated type.
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_hs_generator::generate_enum(t_enum* tenum) {
+ indent(f_types_) << "data " << capitalize(tenum->get_name()) << " = ";
+ indent_up();
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ bool first = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ string name = capitalize((*c_iter)->get_name());
+ f_types_ << (first ? "" : "|");
+ f_types_ << name;
+ first = false;
+ }
+ indent(f_types_) << "deriving (P.Show, P.Eq, G.Generic, TY.Typeable, P.Ord, P.Bounded)" << endl;
+ indent_down();
+
+ string ename = capitalize(tenum->get_name());
+
+ indent(f_types_) << "instance P.Enum " << ename << " where" << endl;
+ indent_up();
+ indent(f_types_) << "fromEnum t = case t of" << endl;
+ indent_up();
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ string name = capitalize((*c_iter)->get_name());
+ indent(f_types_) << name << " -> " << value << endl;
+ }
+ indent_down();
+ indent(f_types_) << "toEnum t = case t of" << endl;
+ indent_up();
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ string name = capitalize((*c_iter)->get_name());
+ indent(f_types_) << value << " -> " << name << endl;
+ }
+ indent(f_types_) << "_ -> X.throw T.ThriftException" << endl;
+ indent_down();
+ indent_down();
+
+ indent(f_types_) << "instance H.Hashable " << ename << " where" << endl;
+ indent_up();
+ indent(f_types_) << "hashWithSalt salt = H.hashWithSalt salt P.. P.fromEnum" << endl;
+ indent_down();
+
+ indent(f_types_) << "instance QC.Arbitrary " << ename << " where" << endl;
+ indent_up();
+ indent(f_types_) << "arbitrary = QC.elements (P.enumFromTo P.minBound P.maxBound)" << endl;
+ indent_down();
+}
+
+/**
+ * Generate a constant value
+ */
+void t_hs_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = decapitalize(tconst->get_name());
+
+ t_const_value* value = tconst->get_value();
+
+ indent(f_consts_) << name << " :: " << render_hs_type(type, false) << endl;
+ indent(f_consts_) << name << " = " << render_const_value(type, value) << endl;
+ f_consts_ << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_hs_generator::render_const_value(t_type* type, t_const_value* value) {
+ if (value == NULL)
+ return type_to_default(type);
+
+ type = get_true_type(type);
+ ostringstream out;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "P.True" : "P.False");
+ break;
+
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << "(" << value->get_integer() << ")";
+ break;
+
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << "(" << value->get_integer() << ")";
+ } else {
+ out << "(" << value->get_double() << ")";
+ }
+ break;
+
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+
+ } else if (type->is_enum()) {
+ t_enum* tenum = (t_enum*)type;
+ vector<t_enum_value*> constants = tenum->get_constants();
+ for (auto & constant : constants) {
+ int val = constant->get_value();
+ if (val == value->get_integer()) {
+ t_program* prog = type->get_program();
+ if (prog != NULL && prog != program_)
+ out << capitalize(prog->get_name()) << "_Types.";
+ out << capitalize(constant->get_name());
+ break;
+ }
+ }
+
+ } else if (type->is_struct() || type->is_xception()) {
+ string cname = type_name(type);
+ out << "default_" << cname << "{";
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+
+ bool first = true;
+ for (auto v_iter : val) {
+ t_field* field = NULL;
+
+ for (auto f_iter : fields)
+ if (f_iter->get_name() == v_iter.first->get_string())
+ field = f_iter;
+
+ if (field == NULL)
+ throw "type error: " + cname + " has no field " + v_iter.first->get_string();
+
+ string fname = v_iter.first->get_string();
+ string const_value = render_const_value(field->get_type(), v_iter.second);
+
+ out << (first ? "" : ", ");
+ out << field_name(cname, fname) << " = ";
+ if (field->get_req() == t_field::T_OPTIONAL || ((t_type*)field->get_type())->is_xception()) {
+ out << "P.Just ";
+ }
+ out << const_value;
+ first = false;
+ }
+
+ out << "}";
+
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ out << "(Map.fromList [";
+
+ bool first = true;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(ktype, v_iter->first);
+ string val = render_const_value(vtype, v_iter->second);
+ out << (first ? "" : ",");
+ out << "(" << key << "," << val << ")";
+ first = false;
+ }
+ out << "])";
+
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype = type->is_list() ? ((t_list*)type)->get_elem_type()
+ : ((t_set*)type)->get_elem_type();
+
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+
+ if (type->is_set())
+ out << "(Set.fromList [";
+ else
+ out << "(Vector.fromList [";
+
+ bool first = true;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << (first ? "" : ",");
+ out << render_const_value(etype, *v_iter);
+ first = false;
+ }
+
+ out << "])";
+
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+
+ return out.str();
+}
+
+/**
+ * Generates a "struct"
+ */
+void t_hs_generator::generate_struct(t_struct* tstruct) {
+ generate_hs_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct, but also has an exception declaration.
+ *
+ * @param txception The struct definition
+ */
+void t_hs_generator::generate_xception(t_struct* txception) {
+ generate_hs_struct(txception, true);
+}
+
+/**
+ * Generates a Haskell struct
+ */
+void t_hs_generator::generate_hs_struct(t_struct* tstruct, bool is_exception) {
+ generate_hs_struct_definition(f_types_, tstruct, is_exception, false);
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_hs_generator::generate_hs_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool helper) {
+ (void)helper;
+ string tname = type_name(tstruct);
+ string name = tstruct->get_name();
+ const vector<t_field*>& members = tstruct->get_members();
+
+ indent(out) << "data " << tname << " = " << tname;
+ if (members.size() > 0) {
+ indent_up();
+ bool first = true;
+ for (auto member : members) {
+ if (first) {
+ indent(out) << "{ ";
+ first = false;
+ } else {
+ indent(out) << ", ";
+ }
+ string mname = member->get_name();
+ out << field_name(tname, mname) << " :: ";
+ if (member->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)member->get_type())->is_xception()) {
+ out << "P.Maybe ";
+ }
+ out << render_hs_type(member->get_type(), true) << endl;
+ }
+ indent(out) << "}";
+ indent_down();
+ }
+
+ out << " deriving (P.Show,P.Eq,G.Generic,TY.Typeable)" << endl;
+
+ if (is_exception)
+ out << "instance X.Exception " << tname << endl;
+
+ indent(out) << "instance H.Hashable " << tname << " where" << endl;
+ indent_up();
+ indent(out) << "hashWithSalt salt record = salt";
+ for (auto member : members) {
+ string mname = member->get_name();
+ indent(out) << " `H.hashWithSalt` " << field_name(tname, mname) << " record";
+ }
+ indent(out) << endl;
+ indent_down();
+
+ generate_hs_struct_arbitrary(out, tstruct);
+ generate_hs_struct_writer(out, tstruct);
+ generate_hs_struct_reader(out, tstruct);
+ generate_hs_typemap(out, tstruct);
+ generate_hs_default(out, tstruct);
+}
+
+void t_hs_generator::generate_hs_struct_arbitrary(ostream& out, t_struct* tstruct) {
+ string tname = type_name(tstruct);
+ string name = tstruct->get_name();
+ const vector<t_field*>& members = tstruct->get_members();
+
+ indent(out) << "instance QC.Arbitrary " << tname << " where " << endl;
+ indent_up();
+ if (members.size() > 0) {
+ indent(out) << "arbitrary = M.liftM " << tname;
+ indent_up();
+ indent_up();
+ indent_up();
+ indent_up();
+ bool first = true;
+ for (auto member : members) {
+ if (first) {
+ first = false;
+ out << " ";
+ } else {
+ indent(out) << "`M.ap`";
+ }
+ out << "(";
+ if (member->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)member->get_type())->is_xception()) {
+ out << "M.liftM P.Just ";
+ }
+ out << "QC.arbitrary)" << endl;
+ }
+ indent_down();
+ indent_down();
+ indent_down();
+ indent_down();
+
+ // Shrink
+ indent(out) << "shrink obj | obj == default_" << tname << " = []" << endl;
+ indent(out) << " | P.otherwise = M.catMaybes" << endl;
+ indent_up();
+ first = true;
+ for (auto member : members) {
+ if (first) {
+ first = false;
+ indent(out) << "[ ";
+ } else {
+ indent(out) << ", ";
+ }
+ string fname = field_name(tname, member->get_name());
+ out << "if obj == default_" << tname;
+ out << "{" << fname << " = " << fname << " obj} ";
+ out << "then P.Nothing ";
+ out << "else P.Just $ default_" << tname;
+ out << "{" << fname << " = " << fname << " obj}" << endl;
+ }
+ indent(out) << "]" << endl;
+ indent_down();
+ } else { /* 0 == members.size() */
+ indent(out) << "arbitrary = QC.elements [" << tname << "]" << endl;
+ }
+ indent_down();
+}
+
+/**
+ * Generates the read method for a struct
+ */
+void t_hs_generator::generate_hs_struct_reader(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+
+ string sname = type_name(tstruct);
+ string id = tmp("_id");
+ string val = tmp("_val");
+
+ indent(out) << "to_" << sname << " :: T.ThriftVal -> " << sname << endl;
+ indent(out) << "to_" << sname << " (T.TStruct fields) = " << sname << "{" << endl;
+ indent_up();
+
+ bool first = true;
+
+ // Generate deserialization code for known cases
+ for (auto field : fields) {
+ int32_t key = field->get_key();
+ string etype = type_to_enum(field->get_type());
+ string fname = field->get_name();
+
+ if (first) {
+ first = false;
+ } else {
+ out << "," << endl;
+ }
+
+ // Fill in Field
+ indent(out) << field_name(sname, fname) << " = ";
+
+ out << "P.maybe (";
+ if (field->get_req() == t_field::T_REQUIRED) {
+ out << "P.error \"Missing required field: " << fname << "\"";
+ } else {
+ if ((field->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)field->get_type())->is_xception()) && field->get_value() == NULL) {
+ out << "P.Nothing";
+ } else {
+ out << field_name(sname, fname) << " default_" << sname;
+ }
+ }
+ out << ") ";
+
+ out << "(\\(_," << val << ") -> ";
+ if (field->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)field->get_type())->is_xception())
+ out << "P.Just ";
+ generate_deserialize_field(out, field, val);
+ out << ")";
+ out << " (Map.lookup (" << key << ") fields)";
+ }
+
+ out << endl;
+ indent(out) << "}" << endl;
+ indent_down();
+
+ // read
+ string tmap = type_name(tstruct, "typemap_");
+ indent(out) << "to_" << sname << " _ = P.error \"not a struct\"" << endl;
+
+ indent(out) << "read_" << sname << " :: T.Protocol p => p -> P.IO " << sname
+ << endl;
+ indent(out) << "read_" << sname << " iprot = to_" << sname;
+ out << " <$> T.readVal iprot (T.T_STRUCT " << tmap << ")" << endl;
+
+ indent(out) << "decode_" << sname
+ << " :: T.StatelessProtocol p => p -> LBS.ByteString -> " << sname << endl;
+ indent(out) << "decode_" << sname << " iprot bs = to_" << sname << " $ ";
+ out << "T.deserializeVal iprot (T.T_STRUCT " << tmap << ") bs" << endl;
+}
+
+void t_hs_generator::generate_hs_struct_writer(ostream& out, t_struct* tstruct) {
+ string name = type_name(tstruct);
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ string str = tmp("_str");
+ string f = tmp("_f");
+ string v = tmp("_v");
+
+ indent(out) << "from_" << name << " :: " << name << " -> T.ThriftVal" << endl;
+ indent(out) << "from_" << name << " record = T.TStruct $ Map.fromList ";
+ indent_up();
+
+ // Get Exceptions
+ bool hasExn = false;
+ for (auto field : fields) {
+ if (((t_type*)field->get_type())->is_xception()) {
+ hasExn = true;
+ break;
+ }
+ }
+
+ bool isfirst = true;
+ if (hasExn) {
+ out << endl;
+ indent(out) << "(let exns = M.catMaybes ";
+ indent_up();
+ for (auto field : fields) {
+ if (((t_type*)field->get_type())->is_xception()) {
+ if (isfirst) {
+ out << "[ ";
+ isfirst = false;
+ } else {
+ out << ", ";
+ }
+ string mname = field->get_name();
+ int32_t key = field->get_key();
+ out << "(\\" << v << " -> (" << key << ", (\"" << mname << "\",";
+ generate_serialize_type(out, field->get_type(), v);
+ out << "))) <$> " << field_name(name, mname) << " record";
+ }
+ }
+ if (!isfirst) {
+ out << "]" << endl;
+ }
+ indent_down();
+ indent(out) << "in if P.not (P.null exns) then exns else ";
+ indent_up();
+ } else {
+ out << "$ ";
+ }
+
+ out << "M.catMaybes" << endl;
+ // Get the Rest
+ isfirst = true;
+ for (auto field : fields) {
+ // Write field header
+ if (isfirst) {
+ indent(out) << "[ ";
+ isfirst = false;
+ } else {
+ indent(out) << ", ";
+ }
+ string mname = field->get_name();
+ int32_t key = field->get_key();
+ out << "(\\";
+ out << v << " -> ";
+ if (field->get_req() != t_field::T_OPTIONAL
+ && !((t_type*)field->get_type())->is_xception()) {
+ out << "P.Just ";
+ }
+ out << "(" << key << ", (\"" << mname << "\",";
+ generate_serialize_type(out, field->get_type(), v);
+ out << "))) ";
+ if (field->get_req() != t_field::T_OPTIONAL
+ && !((t_type*)field->get_type())->is_xception()) {
+ out << "$";
+ } else {
+ out << "<$>";
+ }
+ out << " " << field_name(name, mname) << " record" << endl;
+ }
+
+ // Write the struct map
+ if (isfirst) {
+ indent(out) << "[]" << endl;
+ } else {
+ indent(out) << "]" << endl;
+ }
+ if (hasExn) {
+ indent(out) << ")" << endl;
+ indent_down();
+ }
+ indent_down();
+
+ // write
+ indent(out) << "write_" << name << " :: T.Protocol p => p -> " << name
+ << " -> P.IO ()" << endl;
+ indent(out) << "write_" << name << " oprot record = T.writeVal oprot $ from_";
+ out << name << " record" << endl;
+
+ // encode
+ indent(out) << "encode_" << name << " :: T.StatelessProtocol p => p -> " << name
+ << " -> LBS.ByteString" << endl;
+ indent(out) << "encode_" << name << " oprot record = T.serializeVal oprot $ ";
+ out << "from_" << name << " record" << endl;
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_hs_generator::generate_service(t_service* tservice) {
+ string f_service_name = get_out_dir() + capitalize(service_name_) + ".hs";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << hs_language_pragma() << endl;
+ f_service_ << hs_autogen_comment() << endl;
+ f_service_ << "module " << capitalize(service_name_) << " where" << endl;
+ f_service_ << hs_imports() << endl;
+
+ if (tservice->get_extends()) {
+ f_service_ << "import qualified " << capitalize(tservice->get_extends()->get_name()) << endl;
+ }
+
+ f_service_ << "import " << capitalize(program_name_) << "_Types" << endl;
+ f_service_ << "import qualified " << capitalize(service_name_) << "_Iface as Iface" << endl;
+
+ // Generate the three main parts of the service
+ generate_service_helpers(tservice);
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+
+ // Close service file
+ f_service_.close();
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_hs_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ indent(f_service_) << "-- HELPER FUNCTIONS AND STRUCTURES --" << endl;
+ indent(f_service_) << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_hs_struct_definition(f_service_, ts, false);
+ generate_hs_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_hs_generator::generate_hs_function_helpers(t_function* tfunction) {
+ t_struct result(program_, field_name(tfunction->get_name(), "result"));
+ t_field success(tfunction->get_returntype(), "success", 0);
+
+ if (!tfunction->get_returntype()->is_void())
+ result.append(&success);
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ result.append(*f_iter);
+
+ generate_hs_struct_definition(f_service_, &result, false);
+}
+
+/**
+ * Generate the map from field names to (type, id)
+ * @param tstruct the Struct
+ */
+void t_hs_generator::generate_hs_typemap(ostream& out, t_struct* tstruct) {
+ string name = type_name(tstruct);
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+
+ indent(out) << "typemap_" << name << " :: T.TypeMap" << endl;
+ indent(out) << "typemap_" << name << " = Map.fromList [";
+ bool first = true;
+ for (auto field : fields) {
+ string mname = field->get_name();
+ if (!first) {
+ out << ",";
+ }
+
+ t_type* type = get_true_type(field->get_type());
+ int32_t key = field->get_key();
+ out << "(" << key << ",(\"" << mname << "\"," << type_to_enum(type) << "))";
+ first = false;
+ }
+ out << "]" << endl;
+}
+
+/**
+ * generate the struct with default values filled in
+ * @param tstruct the Struct
+ */
+void t_hs_generator::generate_hs_default(ostream& out, t_struct* tstruct) {
+ string name = type_name(tstruct);
+ string fname = type_name(tstruct, "default_");
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+
+ indent(out) << fname << " :: " << name << endl;
+ indent(out) << fname << " = " << name << "{" << endl;
+ indent_up();
+ bool first = true;
+ for (auto field : fields) {
+ string mname = field->get_name();
+ if (first) {
+ first = false;
+ } else {
+ out << "," << endl;
+ }
+
+ t_type* type = get_true_type(field->get_type());
+ t_const_value* value = field->get_value();
+ indent(out) << field_name(name, mname) << " = ";
+ if (field->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)field->get_type())->is_xception()) {
+ if (value == NULL) {
+ out << "P.Nothing";
+ } else {
+ out << "P.Just " << render_const_value(type, value);
+ }
+ } else {
+ out << render_const_value(type, value);
+ }
+ }
+ out << "}" << endl;
+ indent_down();
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_hs_generator::generate_service_interface(t_service* tservice) {
+ string f_iface_name = get_out_dir() + capitalize(service_name_) + "_Iface.hs";
+ f_iface_.open(f_iface_name.c_str());
+
+ f_iface_ << hs_language_pragma() << endl;
+ f_iface_ << hs_autogen_comment() << endl;
+
+ f_iface_ << "module " << capitalize(service_name_) << "_Iface where" << endl;
+
+ f_iface_ << hs_imports() << endl;
+ f_iface_ << "import " << capitalize(program_name_) << "_Types" << endl;
+ f_iface_ << endl;
+
+ string sname = capitalize(service_name_);
+ if (tservice->get_extends() != NULL) {
+ string extends = type_name(tservice->get_extends());
+
+ indent(f_iface_) << "import " << extends << "_Iface" << endl;
+ indent(f_iface_) << "class " << extends << "_Iface a => " << sname << "_Iface a where" << endl;
+
+ } else {
+ indent(f_iface_) << "class " << sname << "_Iface a where" << endl;
+ }
+
+ indent_up();
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string ft = function_type(*f_iter, true, true, true);
+ indent(f_iface_) << decapitalize((*f_iter)->get_name()) << " :: a -> " << ft << endl;
+ }
+
+ indent_down();
+ f_iface_.close();
+}
+
+/**
+ * Generates a service client definition. Note that in Haskell, the client doesn't implement iface.
+ *This is because
+ * The client does not (and should not have to) deal with arguments being Nothing.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_hs_generator::generate_service_client(t_service* tservice) {
+ string f_client_name = get_out_dir() + capitalize(service_name_) + "_Client.hs";
+ f_client_.open(f_client_name.c_str());
+ f_client_ << hs_language_pragma() << endl;
+ f_client_ << hs_autogen_comment() << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+
+ string extends = "";
+ string exports = "";
+
+ bool first = true;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ exports += (first ? "" : ",");
+ string funname = (*f_iter)->get_name();
+ exports += decapitalize(funname);
+ first = false;
+ }
+
+ string sname = capitalize(service_name_);
+ indent(f_client_) << "module " << sname << "_Client(" << exports << ") where" << endl;
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ indent(f_client_) << "import " << extends << "_Client" << endl;
+ }
+
+ indent(f_client_) << "import qualified Data.IORef as R" << endl;
+ indent(f_client_) << hs_imports() << endl;
+ indent(f_client_) << "import " << capitalize(program_name_) << "_Types" << endl;
+ indent(f_client_) << "import " << capitalize(service_name_) << endl;
+
+ // DATS RITE A GLOBAL VAR
+ indent(f_client_) << "seqid = R.newIORef 0" << endl;
+
+ // Generate client method implementations
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+
+ string fargs = "";
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter)
+ fargs += " arg_" + (*fld_iter)->get_name();
+
+ // Open function
+ indent(f_client_) << decapitalize(funname) << " (ip,op)" << fargs << " = do" << endl;
+ indent_up();
+ indent(f_client_) << "send_" << funname << " op" << fargs;
+
+ f_client_ << endl;
+
+ if (!(*f_iter)->is_oneway())
+ indent(f_client_) << "recv_" << funname << " ip" << endl;
+
+ indent_down();
+
+ indent(f_client_) << "send_" << funname << " op" << fargs << " = do" << endl;
+ indent_up();
+
+ indent(f_client_) << "seq <- seqid" << endl;
+ indent(f_client_) << "seqn <- R.readIORef seq" << endl;
+ string argsname = capitalize((*f_iter)->get_name() + "_args");
+
+ // Serialize the request header
+ string fname = (*f_iter)->get_name();
+ string msgType = (*f_iter)->is_oneway() ? "T.M_ONEWAY" : "T.M_CALL";
+ indent(f_client_) << "T.writeMessage op (\"" << fname << "\", " << msgType << ", seqn) $"
+ << endl;
+ indent_up();
+ indent(f_client_) << "write_" << argsname << " op (" << argsname << "{";
+
+ bool first = true;
+ for (auto field : fields) {
+ string fieldname = field->get_name();
+ f_client_ << (first ? "" : ",");
+ f_client_ << field_name(argsname, fieldname) << "=";
+ if (field->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)field->get_type())->is_xception())
+ f_client_ << "P.Just ";
+ f_client_ << "arg_" << fieldname;
+ first = false;
+ }
+ f_client_ << "})" << endl;
+ indent_down();
+ indent_down();
+
+ if (!(*f_iter)->is_oneway()) {
+ string resultname = capitalize((*f_iter)->get_name() + "_result");
+ t_struct noargs(program_);
+
+ string funname = string("recv_") + (*f_iter)->get_name();
+ t_function recv_function((*f_iter)->get_returntype(), funname, &noargs);
+
+ // Open function
+ indent(f_client_) << funname << " ip = do" << endl;
+ indent_up();
+
+ indent(f_client_) << "T.readMessage ip $ \\(fname, mtype, rseqid) -> do" << endl;
+ indent_up();
+ indent(f_client_) << "M.when (mtype == T.M_EXCEPTION) $ do { exn <- T.readAppExn ip ; "
+ "X.throw exn }" << endl;
+
+ indent(f_client_) << "res <- read_" << resultname << " ip" << endl;
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+
+ for (auto xception : xceptions) {
+ indent(f_client_) << "P.maybe (P.return ()) X.throw ("
+ << field_name(resultname, xception->get_name()) << " res)" << endl;
+ }
+
+ if (!(*f_iter)->get_returntype()->is_void())
+ indent(f_client_) << "P.return $ " << field_name(resultname, "success") << " res" << endl;
+ else
+ indent(f_client_) << "P.return ()" << endl;
+
+ // Close function
+ indent_down();
+ indent_down();
+ }
+ }
+
+ f_client_.close();
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_hs_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ generate_process_function(tservice, *f_iter);
+
+ indent(f_service_) << "proc_ handler (iprot,oprot) (name,typ,seqid) = case name of" << endl;
+ indent_up();
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string fname = (*f_iter)->get_name();
+ indent(f_service_) << "\"" << fname << "\" -> process_" << decapitalize(fname)
+ << " (seqid,iprot,oprot,handler)" << endl;
+ }
+
+ indent(f_service_) << "_ -> ";
+ if (tservice->get_extends() != NULL) {
+ f_service_ << type_name(tservice->get_extends())
+ << ".proc_ handler (iprot,oprot) (name,typ,seqid)" << endl;
+
+ } else {
+ f_service_ << "do" << endl;
+ indent_up();
+ indent(f_service_) << "_ <- T.readVal iprot (T.T_STRUCT Map.empty)" << endl;
+ indent(f_service_) << "T.writeMessage oprot (name,T.M_EXCEPTION,seqid) $" << endl;
+ indent_up();
+ indent(f_service_) << "T.writeAppExn oprot (T.AppExn T.AE_UNKNOWN_METHOD (\"Unknown function "
+ "\" ++ LT.unpack name))" << endl;
+ indent_down();
+ indent_down();
+ }
+
+ indent_down();
+
+ // Generate the server implementation
+ indent(f_service_) << "process handler (iprot, oprot) = do" << endl;
+ indent_up();
+
+ indent(f_service_) << "T.readMessage iprot (" << endl;
+ indent(f_service_) << " proc_ handler (iprot,oprot))" << endl;
+ indent(f_service_) << "P.return P.True" << endl;
+ indent_down();
+}
+
+bool hasNoArguments(t_function* func) {
+ return (func->get_arglist()->get_members().empty());
+}
+
+string t_hs_generator::render_hs_type_for_function_name(t_type* type) {
+ string type_str = render_hs_type(type, false);
+ std::string::size_type found = -1;
+
+ while (true) {
+ found = type_str.find_first_of("[]. ", found + 1);
+ if (string::npos == size_t(found)) {
+ break;
+ }
+
+ if (type_str[found] == '.')
+ type_str[found] = '_';
+ else
+ type_str[found] = 'Z';
+ }
+ return type_str;
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_hs_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open function
+ string funname = decapitalize(tfunction->get_name());
+ indent(f_service_) << "process_" << funname << " (seqid, iprot, oprot, handler) = do" << endl;
+ indent_up();
+
+ string argsname = capitalize(tfunction->get_name()) + "_args";
+ string resultname = capitalize(tfunction->get_name()) + "_result";
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(f_service_) << "args <- read_" << argsname << " iprot" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ size_t n = xceptions.size() + 1;
+ // Try block for a function with exceptions
+ if (n > 0) {
+ for (size_t i = 0; i < n; i++) {
+ indent(f_service_) << "(X.catch" << endl;
+ indent_up();
+ }
+ }
+
+ if (n > 0) {
+ indent(f_service_) << "(do" << endl;
+ indent_up();
+ }
+ indent(f_service_);
+
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void())
+ f_service_ << "val <- ";
+
+ f_service_ << "Iface." << decapitalize(tfunction->get_name()) << " handler";
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ f_service_ << " (" << field_name(argsname, (*f_iter)->get_name()) << " args)";
+
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << endl;
+ indent(f_service_) << "let res = default_" << resultname << "{"
+ << field_name(resultname, "success") << " = val}";
+
+ } else if (!tfunction->is_oneway()) {
+ f_service_ << endl;
+ indent(f_service_) << "let res = default_" << resultname;
+ }
+ f_service_ << endl;
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ indent(f_service_) << "P.return ()";
+ } else {
+ indent(f_service_) << "T.writeMessage oprot (\"" << tfunction->get_name()
+ << "\", T.M_REPLY, seqid) $" << endl;
+ indent_up();
+ indent(f_service_) << "write_" << resultname << " oprot res";
+ indent_down();
+ }
+ if (n > 0) {
+ f_service_ << ")";
+ indent_down();
+ }
+ f_service_ << endl;
+
+ if (n > 0) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent(f_service_) << "(\\e -> do" << endl;
+ indent_up();
+
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << "let res = default_" << resultname << "{"
+ << field_name(resultname, (*x_iter)->get_name()) << " = P.Just e}"
+ << endl;
+ indent(f_service_) << "T.writeMessage oprot (\"" << tfunction->get_name()
+ << "\", T.M_REPLY, seqid) $" << endl;
+ indent_up();
+ indent(f_service_) << "write_" << resultname << " oprot res";
+ indent_down();
+ } else {
+ indent(f_service_) << "P.return ()";
+ }
+
+ f_service_ << "))" << endl;
+ indent_down();
+ indent_down();
+ }
+ indent(f_service_) << "((\\_ -> do" << endl;
+ indent_up();
+
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << "T.writeMessage oprot (\"" << tfunction->get_name()
+ << "\", T.M_EXCEPTION, seqid) $" << endl;
+ indent_up();
+ indent(f_service_) << "T.writeAppExn oprot (T.AppExn T.AE_UNKNOWN \"\")";
+ indent_down();
+ } else {
+ indent(f_service_) << "P.return ()";
+ }
+
+ f_service_ << ") :: X.SomeException -> P.IO ()))" << endl;
+ indent_down();
+ indent_down();
+ }
+ // Close function
+ indent_down();
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_hs_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+ (void)prefix;
+ t_type* type = tfield->get_type();
+ generate_deserialize_type(out, type, prefix);
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_hs_generator::generate_deserialize_type(ostream& out, t_type* type, string arg) {
+ type = get_true_type(type);
+ string val = tmp("_val");
+ out << "(case " << arg << " of {" << type_to_constructor(type) << " " << val << " -> ";
+
+ if (type->is_void())
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE";
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, val);
+
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, val);
+
+ } else if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ if (tbase == t_base_type::TYPE_STRING && !type->is_binary()) {
+ out << "E.decodeUtf8 ";
+ }
+ out << val;
+ if (type->is_binary()) {
+ // Since wire type of binary is the same as string, we actually receive T.TString not
+ // T.TBinary
+ out << "; T.TString " << val << " -> " << val;
+ }
+ } else if (type->is_enum()) {
+ out << "P.toEnum $ P.fromIntegral " << val;
+
+ } else {
+ throw "DO NOT KNOW HOW TO DESERIALIZE TYPE " + type->get_name();
+ }
+ out << "; _ -> P.error \"wrong type\"})";
+}
+
+/**
+ * Generates an unserializer for a struct, calling read()
+ */
+void t_hs_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string name) {
+
+ out << "(" << type_name(tstruct, "to_") << " (T.TStruct " << name << "))";
+}
+
+/**
+ * Serialize a container by writing out the header followed by
+ * data and then a footer.
+ */
+void t_hs_generator::generate_deserialize_container(ostream& out, t_type* ttype, string arg) {
+
+ string val = tmp("_v");
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ string key = tmp("_k");
+ out << "(Map.fromList $ P.map (\\(" << key << "," << val << ") -> (";
+ generate_deserialize_type(out, ((t_map*)ttype)->get_key_type(), key);
+
+ out << ",";
+ generate_deserialize_type(out, ((t_map*)ttype)->get_val_type(), val);
+
+ out << ")) " << arg << ")";
+
+ } else if (ttype->is_set()) {
+ out << "(Set.fromList $ P.map (\\" << val << " -> ";
+ generate_deserialize_type(out, ((t_set*)ttype)->get_elem_type(), val);
+ out << ") " << arg << ")";
+
+ } else if (ttype->is_list()) {
+ out << "(Vector.fromList $ P.map (\\" << val << " -> ";
+ generate_deserialize_type(out, ((t_list*)ttype)->get_elem_type(), val);
+ out << ") " << arg << ")";
+ }
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_hs_generator::generate_serialize_type(ostream& out, t_type* type, string name) {
+
+ type = get_true_type(type);
+ // Do nothing for void types
+ if (type->is_void())
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE";
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name);
+
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, name);
+
+ } else if (type->is_base_type() || type->is_enum()) {
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ out << type_to_constructor(type) << " ";
+ if (tbase == t_base_type::TYPE_STRING && !type->is_binary()) {
+ out << "$ E.encodeUtf8 ";
+ }
+ out << name;
+
+ } else if (type->is_enum()) {
+ string ename = capitalize(type->get_name());
+ out << "T.TI32 $ P.fromIntegral $ P.fromEnum " << name;
+ }
+
+ } else {
+ throw "DO NOT KNOW HOW TO SERIALIZE FIELD OF TYPE " + type->get_name();
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_hs_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ out << type_name(tstruct, "from_") << " " << prefix;
+}
+
+void t_hs_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ string k = tmp("_k");
+ string v = tmp("_v");
+
+ if (ttype->is_map()) {
+ t_type* ktype = ((t_map*)ttype)->get_key_type();
+ t_type* vtype = ((t_map*)ttype)->get_val_type();
+ out << "T.TMap " << type_to_enum(ktype) << " " << type_to_enum(vtype);
+ out << " $ P.map (\\(" << k << "," << v << ") -> (";
+ generate_serialize_type(out, ktype, k);
+ out << ", ";
+ generate_serialize_type(out, vtype, v);
+ out << ")) $ Map.toList " << prefix;
+
+ } else if (ttype->is_set()) {
+ out << "T.TSet " << type_to_enum(((t_set*)ttype)->get_elem_type());
+ out << " $ P.map (\\" << v << " -> ";
+ generate_serialize_type(out, ((t_set*)ttype)->get_elem_type(), v);
+ out << ") $ Set.toList " << prefix;
+
+ } else if (ttype->is_list()) {
+ out << "T.TList " << type_to_enum(((t_list*)ttype)->get_elem_type());
+ out << " $ P.map (\\" << v << " -> ";
+ generate_serialize_type(out, ((t_list*)ttype)->get_elem_type(), v);
+ out << ") $ Vector.toList " << prefix;
+ }
+}
+
+string t_hs_generator::function_type(t_function* tfunc, bool options, bool io, bool method) {
+ string result = "";
+
+ const vector<t_field*>& fields = tfunc->get_arglist()->get_members();
+ for (auto field : fields) {
+ if (field->get_req() == t_field::T_OPTIONAL
+ || ((t_type*)field->get_type())->is_xception())
+ result += "P.Maybe ";
+ result += render_hs_type(field->get_type(), options);
+ result += " -> ";
+ }
+
+ if (fields.empty() && !method)
+ result += "() -> ";
+
+ if (io)
+ result += "P.IO ";
+
+ result += render_hs_type(tfunc->get_returntype(), io);
+ return result;
+}
+
+string t_hs_generator::type_name(t_type* ttype, string function_prefix) {
+ string prefix = "";
+ t_program* program = ttype->get_program();
+
+ if (program != NULL && program != program_)
+ if (!ttype->is_service())
+ prefix = capitalize(program->get_name()) + "_Types.";
+
+ return prefix + function_prefix + capitalize(ttype->get_name());
+}
+
+string t_hs_generator::field_name(string tname, string fname) {
+ return decapitalize(tname) + "_" + fname;
+}
+
+/**
+ * Converts the parse type to a Protocol.t_type enum
+ */
+string t_hs_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "T.T_VOID";
+ case t_base_type::TYPE_STRING:
+ return type->is_binary() ? "T.T_BINARY" : "T.T_STRING";
+ case t_base_type::TYPE_BOOL:
+ return "T.T_BOOL";
+ case t_base_type::TYPE_I8:
+ return "T.T_BYTE";
+ case t_base_type::TYPE_I16:
+ return "T.T_I16";
+ case t_base_type::TYPE_I32:
+ return "T.T_I32";
+ case t_base_type::TYPE_I64:
+ return "T.T_I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "T.T_DOUBLE";
+ }
+
+ } else if (type->is_enum()) {
+ return "T.T_I32";
+
+ } else if (type->is_struct() || type->is_xception()) {
+ return "(T.T_STRUCT " + type_name((t_struct*)type, "typemap_") + ")";
+
+ } else if (type->is_map()) {
+ string ktype = type_to_enum(((t_map*)type)->get_key_type());
+ string vtype = type_to_enum(((t_map*)type)->get_val_type());
+ return "(T.T_MAP " + ktype + " " + vtype + ")";
+
+ } else if (type->is_set()) {
+ return "(T.T_SET " + type_to_enum(((t_set*)type)->get_elem_type()) + ")";
+
+ } else if (type->is_list()) {
+ return "(T.T_LIST " + type_to_enum(((t_list*)type)->get_elem_type()) + ")";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Converts the parse type to a default value
+ */
+string t_hs_generator::type_to_default(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "P.error \"No default value for type T_VOID\"";
+ case t_base_type::TYPE_STRING:
+ return "\"\"";
+ case t_base_type::TYPE_BOOL:
+ return "P.False";
+ case t_base_type::TYPE_I8:
+ return "0";
+ case t_base_type::TYPE_I16:
+ return "0";
+ case t_base_type::TYPE_I32:
+ return "0";
+ case t_base_type::TYPE_I64:
+ return "0";
+ case t_base_type::TYPE_DOUBLE:
+ return "0";
+ }
+
+ } else if (type->is_enum()) {
+ return "(P.toEnum 0)";
+
+ } else if (type->is_struct() || type->is_xception()) {
+ return type_name((t_struct*)type, "default_");
+
+ } else if (type->is_map()) {
+ return "Map.empty";
+
+ } else if (type->is_set()) {
+ return "Set.empty";
+
+ } else if (type->is_list()) {
+ return "Vector.empty";
+ }
+
+ throw "INVALID TYPE IN type_to_default: " + type->get_name();
+}
+
+/**
+ * Converts the parse type to an haskell type
+ */
+string t_hs_generator::render_hs_type(t_type* type, bool needs_parens) {
+ type = get_true_type(type);
+ string type_repr;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "()";
+ case t_base_type::TYPE_STRING:
+ return (type->is_binary() ? "LBS.ByteString" : "LT.Text");
+ case t_base_type::TYPE_BOOL:
+ return "P.Bool";
+ case t_base_type::TYPE_I8:
+ return "I.Int8";
+ case t_base_type::TYPE_I16:
+ return "I.Int16";
+ case t_base_type::TYPE_I32:
+ return "I.Int32";
+ case t_base_type::TYPE_I64:
+ return "I.Int64";
+ case t_base_type::TYPE_DOUBLE:
+ return "P.Double";
+ }
+
+ } else if (type->is_enum()) {
+ return type_name((t_enum*)type);
+
+ } else if (type->is_struct() || type->is_xception()) {
+ return type_name((t_struct*)type);
+
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ type_repr = "Map.HashMap " + render_hs_type(ktype, true) + " " + render_hs_type(vtype, true);
+
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ type_repr = "Set.HashSet " + render_hs_type(etype, true);
+
+ } else if (type->is_list()) {
+ t_type* etype = ((t_list*)type)->get_elem_type();
+ type_repr = "Vector.Vector " + render_hs_type(etype, true);
+
+ } else {
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+ }
+
+ return needs_parens ? "(" + type_repr + ")" : type_repr;
+}
+
+/**
+ * Converts the parse type to a haskell constructor
+ */
+string t_hs_generator::type_to_constructor(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "invalid type: T_VOID";
+ case t_base_type::TYPE_STRING:
+ return type->is_binary() ? "T.TBinary" : "T.TString";
+ case t_base_type::TYPE_BOOL:
+ return "T.TBool";
+ case t_base_type::TYPE_I8:
+ return "T.TByte";
+ case t_base_type::TYPE_I16:
+ return "T.TI16";
+ case t_base_type::TYPE_I32:
+ return "T.TI32";
+ case t_base_type::TYPE_I64:
+ return "T.TI64";
+ case t_base_type::TYPE_DOUBLE:
+ return "T.TDouble";
+ }
+
+ } else if (type->is_enum()) {
+ return "T.TI32";
+
+ } else if (type->is_struct() || type->is_xception()) {
+ return "T.TStruct";
+
+ } else if (type->is_map()) {
+ return "T.TMap _ _";
+
+ } else if (type->is_set()) {
+ return "T.TSet _";
+
+ } else if (type->is_list()) {
+ return "T.TList _";
+ }
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(hs, "Haskell", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc
new file mode 100644
index 000000000..b61a712e3
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc
@@ -0,0 +1,1084 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <map>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/generate/t_generator.h"
+#include "thrift/generate/t_html_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::pair;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+enum input_type { INPUT_UNKNOWN, INPUT_UTF8, INPUT_PLAIN };
+
+/**
+ * HTML code generator
+ *
+ * mostly copy/pasting/tweaking from mcslee's work.
+ */
+class t_html_generator : public t_generator {
+public:
+ t_html_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ standalone_ = false;
+ unsafe_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("standalone") == 0) {
+ standalone_ = true;
+ } else if( iter->first.compare("noescape") == 0) {
+ unsafe_ = true;
+ } else {
+ throw "unknown option html:" + iter->first;
+ }
+ }
+
+
+ out_dir_base_ = "gen-html";
+ input_type_ = INPUT_UNKNOWN;
+
+ escape_.clear();
+ escape_['&'] = "&amp;";
+ escape_['<'] = "&lt;";
+ escape_['>'] = "&gt;";
+ escape_['"'] = "&quot;";
+ escape_['\''] = "&apos;";
+
+ init_allowed__markup();
+ }
+
+ void generate_program() override;
+ void generate_program_toc();
+ void generate_program_toc_row(t_program* tprog);
+ void generate_program_toc_rows(t_program* tprog, std::vector<t_program*>& finished);
+ void generate_index();
+ std::string escape_html(std::string const& str);
+ std::string escape_html_tags(std::string const& str);
+ void generate_css();
+ void generate_css_content(std::ostream& f_target);
+ void generate_style_tag();
+ std::string make_file_link(std::string name);
+ bool is_utf8_sequence(std::string const& str, size_t firstpos);
+ void detect_input_encoding(std::string const& str, size_t firstpos);
+ void init_allowed__markup();
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_service(t_service* tservice) override;
+ void generate_xception(t_struct* txception) override;
+
+ void print_doc(t_doc* tdoc);
+ int print_type(t_type* ttype);
+ void print_const_value(t_type* type, t_const_value* tvalue);
+ void print_fn_args_doc(t_function* tfunction);
+
+private:
+ ofstream_with_content_based_conditional_update f_out_;
+ std::string current_file_;
+ input_type input_type_;
+ std::map<std::string, int> allowed_markup;
+ bool standalone_;
+ bool unsafe_;
+};
+
+/**
+ * Emits the Table of Contents links at the top of the module's page
+ */
+void t_html_generator::generate_program_toc() {
+ f_out_ << "<table class=\"table-bordered table-striped "
+ "table-condensed\"><thead><tr><th>Module</th><th>Services</th>"
+ << "<th>Data types</th><th>Constants</th></tr></thead><tbody>" << endl;
+ generate_program_toc_row(program_);
+ f_out_ << "</tbody></table>" << endl;
+}
+
+/**
+ * Recurses through from the provided program and generates a ToC row
+ * for each discovered program exactly once by maintaining the list of
+ * completed rows in 'finished'
+ */
+void t_html_generator::generate_program_toc_rows(t_program* tprog,
+ std::vector<t_program*>& finished) {
+ for (auto & iter : finished) {
+ if (tprog->get_path() == iter->get_path()) {
+ return;
+ }
+ }
+ finished.push_back(tprog);
+ generate_program_toc_row(tprog);
+ vector<t_program*> includes = tprog->get_includes();
+ for (auto & include : includes) {
+ generate_program_toc_rows(include, finished);
+ }
+}
+
+/**
+ * Emits the Table of Contents links at the top of the module's page
+ */
+void t_html_generator::generate_program_toc_row(t_program* tprog) {
+ string fname = tprog->get_name() + ".html";
+ f_out_ << "<tr>" << endl << "<td>" << tprog->get_name() << "</td><td>";
+ if (!tprog->get_services().empty()) {
+ vector<t_service*> services = tprog->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ string name = get_service_name(*sv_iter);
+ f_out_ << "<a href=\"" << make_file_link(fname) << "#Svc_" << name << "\">" << name
+ << "</a><br/>" << endl;
+ f_out_ << "<ul>" << endl;
+ map<string, string> fn_html;
+ vector<t_function*> functions = (*sv_iter)->get_functions();
+ vector<t_function*>::iterator fn_iter;
+ for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
+ string fn_name = (*fn_iter)->get_name();
+ string html = "<li><a href=\"" + make_file_link(fname) + "#Fn_" + name + "_" + fn_name
+ + "\">" + fn_name + "</a></li>";
+ fn_html.insert(pair<string, string>(fn_name, html));
+ }
+ for (auto & html_iter : fn_html) {
+ f_out_ << html_iter.second << endl;
+ }
+ f_out_ << "</ul>" << endl;
+ }
+ }
+ f_out_ << "</td>" << endl << "<td>";
+ map<string, string> data_types;
+ if (!tprog->get_enums().empty()) {
+ vector<t_enum*> enums = tprog->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ string name = (*en_iter)->get_name();
+ // f_out_ << "<a href=\"" << make_file_link(fname) << "#Enum_" << name << "\">" << name
+ // << "</a><br/>" << endl;
+ string html = "<a href=\"" + make_file_link(fname) + "#Enum_" + name + "\">" + name + "</a>";
+ data_types.insert(pair<string, string>(name, html));
+ }
+ }
+ if (!tprog->get_typedefs().empty()) {
+ vector<t_typedef*> typedefs = tprog->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ string name = (*td_iter)->get_symbolic();
+ // f_out_ << "<a href=\"" << make_file_link(fname) << "#Typedef_" << name << "\">" << name
+ // << "</a><br/>" << endl;
+ string html = "<a href=\"" + make_file_link(fname) + "#Typedef_" + name + "\">" + name
+ + "</a>";
+ data_types.insert(pair<string, string>(name, html));
+ }
+ }
+ if (!tprog->get_objects().empty()) {
+ vector<t_struct*> objects = tprog->get_objects();
+ vector<t_struct*>::iterator o_iter;
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ string name = (*o_iter)->get_name();
+ // f_out_ << "<a href=\"" << make_file_link(fname) << "#Struct_" << name << "\">" << name
+ //<< "</a><br/>" << endl;
+ string html = "<a href=\"" + make_file_link(fname) + "#Struct_" + name + "\">" + name
+ + "</a>";
+ data_types.insert(pair<string, string>(name, html));
+ }
+ }
+ for (auto & data_type : data_types) {
+ f_out_ << data_type.second << "<br/>" << endl;
+ }
+ f_out_ << "</td>" << endl << "<td>";
+ if (!tprog->get_consts().empty()) {
+ map<string, string> const_html;
+ vector<t_const*> consts = tprog->get_consts();
+ vector<t_const*>::iterator con_iter;
+ for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) {
+ string name = (*con_iter)->get_name();
+ string html = "<code><a href=\"" + make_file_link(fname) + "#Const_" + name + "\">" + name
+ + "</a></code>";
+ const_html.insert(pair<string, string>(name, html));
+ }
+ for (auto & con_iter : const_html) {
+ f_out_ << con_iter.second << "<br/>" << endl;
+ }
+ }
+ f_out_ << "</td>" << endl << "</tr>";
+}
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * stream.
+ */
+void t_html_generator::generate_program() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+ current_file_ = program_->get_name() + ".html";
+ string fname = get_out_dir() + current_file_;
+ f_out_.open(fname.c_str());
+ f_out_ << "<!DOCTYPE html>" << endl;
+ f_out_ << "<html lang=\"en\">" << endl;
+ f_out_ << "<head>" << endl;
+ f_out_ << "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />" << endl;
+ generate_style_tag();
+ f_out_ << "<title>Thrift module: " << program_->get_name() << "</title></head><body>" << endl
+ << "<div class=\"container-fluid\">" << endl
+ << "<h1>Thrift module: " << program_->get_name() << "</h1>" << endl;
+
+ print_doc(program_);
+
+ generate_program_toc();
+
+ if (!program_->get_consts().empty()) {
+ f_out_ << "<hr/><h2 id=\"Constants\">Constants</h2>" << endl;
+ vector<t_const*> consts = program_->get_consts();
+ f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
+ f_out_ << "<thead><tr><th>Constant</th><th>Type</th><th>Value</th></tr></thead><tbody>" << endl;
+ generate_consts(consts);
+ f_out_ << "</tbody></table>";
+ }
+
+ if (!program_->get_enums().empty()) {
+ f_out_ << "<hr/><h2 id=\"Enumerations\">Enumerations</h2>" << endl;
+ // Generate enums
+ vector<t_enum*> enums = program_->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ generate_enum(*en_iter);
+ }
+ }
+
+ if (!program_->get_typedefs().empty()) {
+ f_out_ << "<hr/><h2 id=\"Typedefs\">Type declarations</h2>" << endl;
+ // Generate typedefs
+ vector<t_typedef*> typedefs = program_->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ generate_typedef(*td_iter);
+ }
+ }
+
+ if (!program_->get_objects().empty()) {
+ f_out_ << "<hr/><h2 id=\"Structs\">Data structures</h2>" << endl;
+ // Generate structs and exceptions in declared order
+ vector<t_struct*> objects = program_->get_objects();
+ vector<t_struct*>::iterator o_iter;
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ if ((*o_iter)->is_xception()) {
+ generate_xception(*o_iter);
+ } else {
+ generate_struct(*o_iter);
+ }
+ }
+ }
+
+ if (!program_->get_services().empty()) {
+ f_out_ << "<hr/><h2 id=\"Services\">Services</h2>" << endl;
+ // Generate services
+ vector<t_service*> services = program_->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ service_name_ = get_service_name(*sv_iter);
+ generate_service(*sv_iter);
+ }
+ }
+
+ f_out_ << "</div></body></html>" << endl;
+ f_out_.close();
+
+ generate_index();
+ generate_css();
+}
+
+/**
+ * Emits the index.html file for the recursive set of Thrift programs
+ */
+void t_html_generator::generate_index() {
+ current_file_ = "index.html";
+ string index_fname = get_out_dir() + current_file_;
+ f_out_.open(index_fname.c_str());
+ f_out_ << "<!DOCTYPE html>" << endl << "<html lang=\"en\"><head>" << endl;
+ generate_style_tag();
+ f_out_ << "<title>All Thrift declarations</title></head><body>" << endl
+ << "<div class=\"container-fluid\">" << endl << "<h1>All Thrift declarations</h1>" << endl;
+ f_out_ << "<table class=\"table-bordered table-striped "
+ "table-condensed\"><thead><tr><th>Module</th><th>Services</th><th>Data types</th>"
+ << "<th>Constants</th></tr></thead><tbody>" << endl;
+ vector<t_program*> programs;
+ generate_program_toc_rows(program_, programs);
+ f_out_ << "</tbody></table>" << endl;
+ f_out_ << "</div></body></html>" << endl;
+ f_out_.close();
+}
+
+void t_html_generator::generate_css() {
+ if (!standalone_) {
+ current_file_ = "style.css";
+ string css_fname = get_out_dir() + current_file_;
+ f_out_.open(css_fname.c_str());
+ generate_css_content(f_out_);
+ f_out_.close();
+ }
+}
+
+void t_html_generator::generate_css_content(std::ostream& f_target) {
+ f_target << BOOTSTRAP_CSS() << endl;
+ f_target << "/* Auto-generated CSS for generated Thrift docs */" << endl;
+ f_target << "h3, h4 { margin-bottom: 6px; }" << endl;
+ f_target << "div.definition { border: 1px solid #CCC; margin-bottom: 10px; padding: 10px; }"
+ << endl;
+ f_target << "div.extends { margin: -0.5em 0 1em 5em }" << endl;
+ f_target << "td { vertical-align: top; }" << endl;
+ f_target << "table { empty-cells: show; }" << endl;
+ f_target << "code { line-height: 20px; }" << endl;
+ f_target << ".table-bordered th, .table-bordered td { border-bottom: 1px solid #DDDDDD; }"
+ << endl;
+}
+
+/**
+ * Generates the CSS tag.
+ * Depending on "standalone", either a CSS file link (default), or the entire CSS is embedded
+ * inline.
+ */
+void t_html_generator::generate_style_tag() {
+ if (!standalone_) {
+ f_out_ << "<link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\"/>" << endl;
+ } else {
+ f_out_ << "<style type=\"text/css\"/><!--" << endl;
+ generate_css_content(f_out_);
+ f_out_ << "--></style>" << endl;
+ }
+}
+
+/**
+ * Returns the target file for a <a href> link
+ * The returned string is empty, whenever filename refers to the current file.
+ */
+std::string t_html_generator::make_file_link(std::string filename) {
+ return (current_file_.compare(filename) != 0) ? filename : "";
+}
+
+/**
+ * If the provided documentable object has documentation attached, this
+ * will emit it to the output stream in HTML format.
+ */
+void t_html_generator::print_doc(t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ if (unsafe_) {
+ f_out_ << tdoc->get_doc() << "<br/>";
+ } else {
+ f_out_ << "<pre>" << escape_html(tdoc->get_doc()) << "</pre><br/>";
+ }
+ }
+}
+
+bool t_html_generator::is_utf8_sequence(std::string const& str, size_t firstpos) {
+ // leading char determines the length of the sequence
+ unsigned char c = str.at(firstpos);
+ int count = 0;
+ if ((c & 0xE0) == 0xC0) {
+ count = 1;
+ } else if ((c & 0xF0) == 0xE0) {
+ count = 2;
+ } else if ((c & 0xF8) == 0xF0) {
+ count = 3;
+ } else if ((c & 0xFC) == 0xF8) {
+ count = 4;
+ } else if ((c & 0xFE) == 0xFC) {
+ count = 5;
+ } else {
+ // pdebug("UTF-8 test: char '%c' (%d) is not a valid UTF-8 leading byte", c, int(c));
+ return false; // no UTF-8
+ }
+
+ // following chars
+ size_t pos = firstpos + 1;
+ while ((pos < str.length()) && (0 < count)) {
+ c = str.at(pos);
+ if ((c & 0xC0) != 0x80) {
+ // pdebug("UTF-8 test: char '%c' (%d) is not a valid UTF-8 following byte", c, int(c));
+ return false; // no UTF-8
+ }
+ --count;
+ ++pos;
+ }
+
+ // true if the sequence is complete
+ return (0 == count);
+}
+
+void t_html_generator::detect_input_encoding(std::string const& str, size_t firstpos) {
+ if (is_utf8_sequence(str, firstpos)) {
+ pdebug("Input seems to be already UTF-8 encoded");
+ input_type_ = INPUT_UTF8;
+ return;
+ }
+
+ // fallback
+ pwarning(1, "Input is not UTF-8, treating as plain ANSI");
+ input_type_ = INPUT_PLAIN;
+}
+
+void t_html_generator::init_allowed__markup() {
+ allowed_markup.clear();
+ // standalone tags
+ allowed_markup["br"] = 1;
+ allowed_markup["br/"] = 1;
+ allowed_markup["img"] = 1;
+ // paired tags
+ allowed_markup["b"] = 1;
+ allowed_markup["/b"] = 1;
+ allowed_markup["u"] = 1;
+ allowed_markup["/u"] = 1;
+ allowed_markup["i"] = 1;
+ allowed_markup["/i"] = 1;
+ allowed_markup["s"] = 1;
+ allowed_markup["/s"] = 1;
+ allowed_markup["big"] = 1;
+ allowed_markup["/big"] = 1;
+ allowed_markup["small"] = 1;
+ allowed_markup["/small"] = 1;
+ allowed_markup["sup"] = 1;
+ allowed_markup["/sup"] = 1;
+ allowed_markup["sub"] = 1;
+ allowed_markup["/sub"] = 1;
+ allowed_markup["pre"] = 1;
+ allowed_markup["/pre"] = 1;
+ allowed_markup["tt"] = 1;
+ allowed_markup["/tt"] = 1;
+ allowed_markup["ul"] = 1;
+ allowed_markup["/ul"] = 1;
+ allowed_markup["ol"] = 1;
+ allowed_markup["/ol"] = 1;
+ allowed_markup["li"] = 1;
+ allowed_markup["/li"] = 1;
+ allowed_markup["a"] = 1;
+ allowed_markup["/a"] = 1;
+ allowed_markup["p"] = 1;
+ allowed_markup["/p"] = 1;
+ allowed_markup["code"] = 1;
+ allowed_markup["/code"] = 1;
+ allowed_markup["dl"] = 1;
+ allowed_markup["/dl"] = 1;
+ allowed_markup["dt"] = 1;
+ allowed_markup["/dt"] = 1;
+ allowed_markup["dd"] = 1;
+ allowed_markup["/dd"] = 1;
+ allowed_markup["h1"] = 1;
+ allowed_markup["/h1"] = 1;
+ allowed_markup["h2"] = 1;
+ allowed_markup["/h2"] = 1;
+ allowed_markup["h3"] = 1;
+ allowed_markup["/h3"] = 1;
+ allowed_markup["h4"] = 1;
+ allowed_markup["/h4"] = 1;
+ allowed_markup["h5"] = 1;
+ allowed_markup["/h5"] = 1;
+ allowed_markup["h6"] = 1;
+ allowed_markup["/h6"] = 1;
+}
+
+std::string t_html_generator::escape_html_tags(std::string const& str) {
+ std::ostringstream result;
+
+ unsigned char c = '?';
+ size_t lastpos;
+ size_t firstpos = 0;
+ while (firstpos < str.length()) {
+
+ // look for non-ASCII char
+ lastpos = firstpos;
+ while (lastpos < str.length()) {
+ c = str.at(lastpos);
+ if (('<' == c) || ('>' == c)) {
+ break;
+ }
+ ++lastpos;
+ }
+
+ // copy what we got so far
+ if (lastpos > firstpos) {
+ result << str.substr(firstpos, lastpos - firstpos);
+ firstpos = lastpos;
+ }
+
+ // reached the end?
+ if (firstpos >= str.length()) {
+ break;
+ }
+
+ // tag end without corresponding begin
+ ++firstpos;
+ if ('>' == c) {
+ result << "&gt;";
+ continue;
+ }
+
+ // extract the tag
+ std::ostringstream tagstream;
+ while (firstpos < str.length()) {
+ c = str.at(firstpos);
+ ++firstpos;
+ if ('<' == c) {
+ tagstream << "&lt;"; // nested begin?
+ } else if ('>' == c) {
+ break;
+ } else {
+ tagstream << c; // not very efficient, but tags should be quite short
+ }
+ }
+
+ // we allow for several markup in docstrings, all else will become escaped
+ string tag_content = tagstream.str();
+ string tag_key = tag_content;
+ size_t first_white = tag_key.find_first_of(" \t\f\v\n\r");
+ if (first_white != string::npos) {
+ tag_key.erase(first_white);
+ }
+ for (char & i : tag_key) {
+ i = tolower(i);
+ }
+ if (allowed_markup.find(tag_key) != allowed_markup.end()) {
+ result << "<" << tag_content << ">";
+ } else {
+ result << "&lt;" << tagstream.str() << "&gt;";
+ pverbose("illegal markup <%s> in doc-comment\n", tag_key.c_str());
+ }
+ }
+
+ return result.str();
+}
+
+std::string t_html_generator::escape_html(std::string const& str) {
+ // the generated HTML header says it is UTF-8 encoded
+ // if UTF-8 input has been detected before, we don't need to change anything
+ if (input_type_ == INPUT_UTF8) {
+ return escape_html_tags(str);
+ }
+
+ // convert unsafe chars to their &#<num>; equivalent
+ std::ostringstream result;
+ unsigned char c = '?';
+ unsigned int ic = 0;
+ size_t lastpos;
+ size_t firstpos = 0;
+ while (firstpos < str.length()) {
+
+ // look for non-ASCII char
+ lastpos = firstpos;
+ while (lastpos < str.length()) {
+ c = str.at(lastpos);
+ ic = c;
+ if ((32 > ic) || (127 < ic)) {
+ break;
+ }
+ ++lastpos;
+ }
+
+ // copy what we got so far
+ if (lastpos > firstpos) {
+ result << str.substr(firstpos, lastpos - firstpos);
+ firstpos = lastpos;
+ }
+
+ // reached the end?
+ if (firstpos >= str.length()) {
+ break;
+ }
+
+ // some control code?
+ if (ic <= 31) {
+ switch (c) {
+ case '\r':
+ case '\n':
+ case '\t':
+ result << c;
+ break;
+ default: // silently consume all other ctrl chars
+ break;
+ }
+ ++firstpos;
+ continue;
+ }
+
+ // reached the end?
+ if (firstpos >= str.length()) {
+ break;
+ }
+
+ // try to detect input encoding
+ if (input_type_ == INPUT_UNKNOWN) {
+ detect_input_encoding(str, firstpos);
+ if (input_type_ == INPUT_UTF8) {
+ lastpos = str.length();
+ result << str.substr(firstpos, lastpos - firstpos);
+ break;
+ }
+ }
+
+ // convert the character to something useful based on the detected encoding
+ switch (input_type_) {
+ case INPUT_PLAIN:
+ result << "&#" << ic << ";";
+ ++firstpos;
+ break;
+ default:
+ throw "Unexpected or unrecognized input encoding";
+ }
+ }
+
+ return escape_html_tags(result.str());
+}
+
+/**
+ * Prints out the provided type in HTML
+ */
+int t_html_generator::print_type(t_type* ttype) {
+ std::string::size_type len = 0;
+ f_out_ << "<code>";
+ if (ttype->is_container()) {
+ if (ttype->is_list()) {
+ f_out_ << "list&lt;";
+ len = 6 + print_type(((t_list*)ttype)->get_elem_type());
+ f_out_ << "&gt;";
+ } else if (ttype->is_set()) {
+ f_out_ << "set&lt;";
+ len = 5 + print_type(((t_set*)ttype)->get_elem_type());
+ f_out_ << "&gt;";
+ } else if (ttype->is_map()) {
+ f_out_ << "map&lt;";
+ len = 5 + print_type(((t_map*)ttype)->get_key_type());
+ f_out_ << ", ";
+ len += print_type(((t_map*)ttype)->get_val_type());
+ f_out_ << "&gt;";
+ }
+ } else if (ttype->is_base_type()) {
+ f_out_ << (ttype->is_binary() ? "binary" : ttype->get_name());
+ len = ttype->get_name().size();
+ } else {
+ string prog_name = ttype->get_program()->get_name();
+ string type_name = ttype->get_name();
+ f_out_ << "<a href=\"" << make_file_link(prog_name + ".html") << "#";
+ if (ttype->is_typedef()) {
+ f_out_ << "Struct_";
+ } else if (ttype->is_struct() || ttype->is_xception()) {
+ f_out_ << "Struct_";
+ } else if (ttype->is_enum()) {
+ f_out_ << "Enum_";
+ } else if (ttype->is_service()) {
+ f_out_ << "Svc_";
+ }
+ f_out_ << type_name << "\">";
+ len = type_name.size();
+ if (ttype->get_program() != program_) {
+ f_out_ << prog_name << ".";
+ len += prog_name.size() + 1;
+ }
+ f_out_ << type_name << "</a>";
+ }
+ f_out_ << "</code>";
+ return (int)len;
+}
+
+/**
+ * Prints out an HTML representation of the provided constant value
+ */
+void t_html_generator::print_const_value(t_type* type, t_const_value* tvalue) {
+
+ // if tvalue is an identifier, the constant content is already shown elsewhere
+ if (tvalue->get_type() == t_const_value::CV_IDENTIFIER) {
+ string fname = program_->get_name() + ".html";
+ string name = escape_html(tvalue->get_identifier());
+ f_out_ << "<code><a href=\"" + make_file_link(fname) + "#Const_" + name + "\">" + name
+ + "</a></code>";
+ return;
+ }
+
+ t_type* truetype = type;
+ while (truetype->is_typedef()) {
+ truetype = ((t_typedef*)truetype)->get_type();
+ }
+
+ bool first = true;
+ if (truetype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)truetype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ f_out_ << '"' << escape_html(get_escaped_string(tvalue)) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ f_out_ << ((tvalue->get_integer() != 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ f_out_ << tvalue->get_integer();
+ break;
+ case t_base_type::TYPE_I16:
+ f_out_ << tvalue->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ f_out_ << tvalue->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ f_out_ << tvalue->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (tvalue->get_type() == t_const_value::CV_INTEGER) {
+ f_out_ << tvalue->get_integer();
+ } else {
+ f_out_ << tvalue->get_double();
+ }
+ break;
+ default:
+ f_out_ << "UNKNOWN BASE TYPE";
+ break;
+ }
+ } else if (truetype->is_enum()) {
+ f_out_ << escape_html(truetype->get_name()) << "."
+ << escape_html(tvalue->get_identifier_name());
+ } else if (truetype->is_struct() || truetype->is_xception()) {
+ f_out_ << "{ ";
+ const vector<t_field*>& fields = ((t_struct*)truetype)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = tvalue->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + truetype->get_name() + " has no field "
+ + v_iter->first->get_string();
+ }
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ f_out_ << escape_html(v_iter->first->get_string()) << " = ";
+ print_const_value(field_type, v_iter->second);
+ }
+ f_out_ << " }";
+ } else if (truetype->is_map()) {
+ f_out_ << "{ ";
+ map<t_const_value*, t_const_value*, t_const_value::value_compare> map_elems = tvalue->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator map_iter;
+ for (map_iter = map_elems.begin(); map_iter != map_elems.end(); map_iter++) {
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ print_const_value(((t_map*)truetype)->get_key_type(), map_iter->first);
+ f_out_ << " = ";
+ print_const_value(((t_map*)truetype)->get_val_type(), map_iter->second);
+ }
+ f_out_ << " }";
+ } else if (truetype->is_list()) {
+ f_out_ << "{ ";
+ vector<t_const_value*> list_elems = tvalue->get_list();
+ ;
+ vector<t_const_value*>::iterator list_iter;
+ for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ print_const_value(((t_list*)truetype)->get_elem_type(), *list_iter);
+ }
+ f_out_ << " }";
+ } else if (truetype->is_set()) {
+ f_out_ << "{ ";
+ vector<t_const_value*> list_elems = tvalue->get_list();
+ ;
+ vector<t_const_value*>::iterator list_iter;
+ for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ print_const_value(((t_set*)truetype)->get_elem_type(), *list_iter);
+ }
+ f_out_ << " }";
+ } else {
+ f_out_ << "UNKNOWN TYPE";
+ }
+}
+
+/**
+ * Prints out documentation for arguments/exceptions of a function, if any documentation has been
+ * supplied.
+ */
+void t_html_generator::print_fn_args_doc(t_function* tfunction) {
+ bool has_docs = false;
+ vector<t_field*> args = tfunction->get_arglist()->get_members();
+ vector<t_field*>::iterator arg_iter = args.begin();
+ if (arg_iter != args.end()) {
+ for (; arg_iter != args.end(); arg_iter++) {
+ if ((*arg_iter)->has_doc() && !(*arg_iter)->get_doc().empty())
+ has_docs = true;
+ }
+ if (has_docs) {
+ arg_iter = args.begin();
+ f_out_ << "<br/><h4 id=\"Parameters_" << service_name_ << "_" << tfunction->get_name()
+ << "\">Parameters</h4>" << endl;
+ f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
+ f_out_ << "<thead><tr><th>Name</th><th>Description</th></tr></thead><tbody>";
+ for (; arg_iter != args.end(); arg_iter++) {
+ f_out_ << "<tr><td>" << (*arg_iter)->get_name();
+ f_out_ << "</td><td>";
+ f_out_ << escape_html((*arg_iter)->get_doc());
+ f_out_ << "</td></tr>" << endl;
+ }
+ f_out_ << "</tbody></table>";
+ }
+ }
+
+ has_docs = false;
+ vector<t_field*> excepts = tfunction->get_xceptions()->get_members();
+ vector<t_field*>::iterator ex_iter = excepts.begin();
+ if (ex_iter != excepts.end()) {
+ for (; ex_iter != excepts.end(); ex_iter++) {
+ if ((*ex_iter)->has_doc() && !(*ex_iter)->get_doc().empty())
+ has_docs = true;
+ }
+ if (has_docs) {
+ ex_iter = excepts.begin();
+ f_out_ << "<br/><h4 id=\"Exceptions_" << service_name_ << "_" << tfunction->get_name()
+ << "\">Exceptions</h4>" << endl;
+ f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
+ f_out_ << "<thead><tr><th>Type</th><th>Description</th></tr></thead><tbody>";
+ for (; ex_iter != excepts.end(); ex_iter++) {
+ f_out_ << "<tr><td>" << (*ex_iter)->get_type()->get_name();
+ f_out_ << "</td><td>";
+ f_out_ << escape_html((*ex_iter)->get_doc());
+ f_out_ << "</td></tr>" << endl;
+ }
+ f_out_ << "</tbody></table>";
+ }
+ }
+}
+
+/**
+ * Generates a typedef.
+ *
+ * @param ttypedef The type definition
+ */
+void t_html_generator::generate_typedef(t_typedef* ttypedef) {
+ string name = ttypedef->get_name();
+ f_out_ << "<div class=\"definition\">";
+ f_out_ << "<h3 id=\"Typedef_" << name << "\">Typedef: " << name << "</h3>" << endl;
+ f_out_ << "<p><strong>Base type:</strong>&nbsp;";
+ print_type(ttypedef->get_type());
+ f_out_ << "</p>" << endl;
+ print_doc(ttypedef);
+ f_out_ << "</div>" << endl;
+}
+
+/**
+ * Generates code for an enumerated type.
+ *
+ * @param tenum The enumeration
+ */
+void t_html_generator::generate_enum(t_enum* tenum) {
+ string name = tenum->get_name();
+ f_out_ << "<div class=\"definition\">";
+ f_out_ << "<h3 id=\"Enum_" << name << "\">Enumeration: " << name << "</h3>" << endl;
+ print_doc(tenum);
+ vector<t_enum_value*> values = tenum->get_constants();
+ vector<t_enum_value*>::iterator val_iter;
+ f_out_ << "<br/><table class=\"table-bordered table-striped table-condensed\">" << endl;
+ for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
+ f_out_ << "<tr><td><code>";
+ f_out_ << (*val_iter)->get_name();
+ f_out_ << "</code></td><td><code>";
+ f_out_ << (*val_iter)->get_value();
+ f_out_ << "</code></td><td>" << endl;
+ print_doc((*val_iter));
+ f_out_ << "</td></tr>" << endl;
+ }
+ f_out_ << "</table></div>" << endl;
+}
+
+/**
+ * Generates a constant value
+ */
+void t_html_generator::generate_const(t_const* tconst) {
+ string name = tconst->get_name();
+ f_out_ << "<tr id=\"Const_" << name << "\"><td><code>" << name << "</code></td><td>";
+ print_type(tconst->get_type());
+ f_out_ << "</td><td><code>";
+ print_const_value(tconst->get_type(), tconst->get_value());
+ f_out_ << "</code></td></tr>";
+ if (tconst->has_doc()) {
+ f_out_ << "<tr><td colspan=\"3\"><blockquote>";
+ print_doc(tconst);
+ f_out_ << "</blockquote></td></tr>";
+ }
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_html_generator::generate_struct(t_struct* tstruct) {
+ string name = tstruct->get_name();
+ f_out_ << "<div class=\"definition\">";
+ f_out_ << "<h3 id=\"Struct_" << name << "\">";
+ if (tstruct->is_xception()) {
+ f_out_ << "Exception: ";
+ } else if (tstruct->is_union()) {
+ f_out_ << "Union: ";
+ } else {
+ f_out_ << "Struct: ";
+ }
+ f_out_ << name << "</h3>" << endl;
+ vector<t_field*> members = tstruct->get_members();
+ vector<t_field*>::iterator mem_iter = members.begin();
+ f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
+ f_out_ << "<thead><tr><th>Key</th><th>Field</th><th>Type</th><th>Description</th><th>Requiredness</"
+ "th><th>Default value</th></tr></thead><tbody>" << endl;
+ for (; mem_iter != members.end(); mem_iter++) {
+ f_out_ << "<tr><td>" << (*mem_iter)->get_key() << "</td><td>";
+ f_out_ << (*mem_iter)->get_name();
+ f_out_ << "</td><td>";
+ print_type((*mem_iter)->get_type());
+ f_out_ << "</td><td>";
+ f_out_ << escape_html((*mem_iter)->get_doc());
+ f_out_ << "</td><td>";
+ if ((*mem_iter)->get_req() == t_field::T_OPTIONAL) {
+ f_out_ << "optional";
+ } else if ((*mem_iter)->get_req() == t_field::T_REQUIRED) {
+ f_out_ << "required";
+ } else {
+ f_out_ << "default";
+ }
+ f_out_ << "</td><td>";
+ t_const_value* default_val = (*mem_iter)->get_value();
+ if (default_val != NULL) {
+ f_out_ << "<code>";
+ print_const_value((*mem_iter)->get_type(), default_val);
+ f_out_ << "</code>";
+ }
+ f_out_ << "</td></tr>" << endl;
+ }
+ f_out_ << "</tbody></table><br/>";
+ print_doc(tstruct);
+ f_out_ << "</div>";
+}
+
+/**
+ * Exceptions are special structs
+ *
+ * @param tstruct The struct definition
+ */
+void t_html_generator::generate_xception(t_struct* txception) {
+ generate_struct(txception);
+}
+
+/**
+ * Generates the HTML block for a Thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_html_generator::generate_service(t_service* tservice) {
+ f_out_ << "<h3 id=\"Svc_" << service_name_ << "\">Service: " << service_name_ << "</h3>" << endl;
+
+ if (tservice->get_extends()) {
+ f_out_ << "<div class=\"extends\"><em>extends</em> ";
+ print_type(tservice->get_extends());
+ f_out_ << "</div>\n";
+ }
+ print_doc(tservice);
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator fn_iter = functions.begin();
+ for (; fn_iter != functions.end(); fn_iter++) {
+ string fn_name = (*fn_iter)->get_name();
+ f_out_ << "<div class=\"definition\">";
+ f_out_ << "<h4 id=\"Fn_" << service_name_ << "_" << fn_name << "\">Function: " << service_name_
+ << "." << fn_name << "</h4>" << endl;
+ f_out_ << "<pre>";
+ std::string::size_type offset = print_type((*fn_iter)->get_returntype());
+ bool first = true;
+ f_out_ << " " << fn_name << "(";
+ offset += fn_name.size() + 2;
+ vector<t_field*> args = (*fn_iter)->get_arglist()->get_members();
+ vector<t_field*>::iterator arg_iter = args.begin();
+ for (; arg_iter != args.end(); arg_iter++) {
+ if (!first) {
+ f_out_ << "," << endl;
+ for (std::string::size_type i = 0; i < offset; ++i) {
+ f_out_ << " ";
+ }
+ }
+ first = false;
+ print_type((*arg_iter)->get_type());
+ f_out_ << " " << (*arg_iter)->get_name();
+ if ((*arg_iter)->get_value() != NULL) {
+ f_out_ << " = ";
+ print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value());
+ }
+ }
+ f_out_ << ")" << endl;
+ first = true;
+ vector<t_field*> excepts = (*fn_iter)->get_xceptions()->get_members();
+ vector<t_field*>::iterator ex_iter = excepts.begin();
+ if (ex_iter != excepts.end()) {
+ f_out_ << " throws ";
+ for (; ex_iter != excepts.end(); ex_iter++) {
+ if (!first) {
+ f_out_ << ", ";
+ }
+ first = false;
+ print_type((*ex_iter)->get_type());
+ }
+ f_out_ << endl;
+ }
+ f_out_ << "</pre>";
+ print_doc(*fn_iter);
+ print_fn_args_doc(*fn_iter);
+ f_out_ << "</div>";
+ }
+}
+
+THRIFT_REGISTER_GENERATOR(
+ html,
+ "HTML",
+ " standalone: Self-contained mode, includes all CSS in the HTML files.\n"
+ " Generates no style.css file, but HTML files will be larger.\n"
+ " noescape: Do not escape html in doc text.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h
new file mode 100644
index 000000000..600b17f17
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h
@@ -0,0 +1,240 @@
+#define BOOTSTRAP_CSS() \
+ "/*!\n" \
+ " * Bootstrap v2.0.3\n" \
+ " *\n" \
+ " * Copyright 2012 Twitter, Inc\n" \
+ " * Licensed under the Apache License v2.0\n" \
+ " * http://www.apache.org/licenses/LICENSE-2.0\n" \
+ " *\n" \
+ " * Designed and built with all the love in the world @twitter by @mdo and @fat.\n" \
+ " */\n" \
+ ".clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:\"\";}\n" \
+ ".clearfix:after{clear:both;}\n" \
+ ".hide-text{font:0/0 " \
+ "a;color:transparent;text-shadow:none;background-color:transparent;border:0;}\n" \
+ ".input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-" \
+ "moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}\n" \
+ "article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}\n" \
+ "audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}\n" \
+ "audio:not([controls]){display:none;}\n" \
+ "html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}\n" \
+ "a:focus{outline:thin dotted #333;outline:5px auto " \
+ "-webkit-focus-ring-color;outline-offset:-2px;}\n" \
+ "a:hover,a:active{outline:0;}\n" \
+ "sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}\n" \
+ "sup{top:-0.5em;}\n" \
+ "sub{bottom:-0.25em;}\n" \
+ "img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}\n" \
+ "button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}\n" \
+ "button,input{*overflow:visible;line-height:normal;}\n" \
+ "button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}\n" \
+ "button,input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{cursor:pointer;-" \
+ "webkit-appearance:button;}\n" \
+ "input[type=\"search\"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:" \
+ "content-box;-webkit-appearance:textfield;}\n" \
+ "input[type=\"search\"]::-webkit-search-decoration,input[type=\"search\"]::-webkit-search-" \
+ "cancel-button{-webkit-appearance:none;}\n" \
+ "textarea{overflow:auto;vertical-align:top;}\n" \
+ "body{margin:0;font-family:\"Helvetica " \
+ "Neue\",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-" \
+ "color:#ffffff;}\n" \
+ "a{color:#0088cc;text-decoration:none;}\n" \
+ "a:hover{color:#005580;text-decoration:underline;}\n" \
+ ".row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:\"\";}\n" \
+ ".row:after{clear:both;}\n" \
+ "[class*=\"span\"]{float:left;margin-left:20px;}\n" \
+ ".container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}\n" \
+ ".span12{width:940px;}\n" \
+ ".span11{width:860px;}\n" \
+ ".span10{width:780px;}\n" \
+ ".span9{width:700px;}\n" \
+ ".span8{width:620px;}\n" \
+ ".span7{width:540px;}\n" \
+ ".span6{width:460px;}\n" \
+ ".span5{width:380px;}\n" \
+ ".span4{width:300px;}\n" \
+ ".span3{width:220px;}\n" \
+ ".span2{width:140px;}\n" \
+ ".span1{width:60px;}\n" \
+ ".offset12{margin-left:980px;}\n" \
+ ".offset11{margin-left:900px;}\n" \
+ ".offset10{margin-left:820px;}\n" \
+ ".offset9{margin-left:740px;}\n" \
+ ".offset8{margin-left:660px;}\n" \
+ ".offset7{margin-left:580px;}\n" \
+ ".offset6{margin-left:500px;}\n" \
+ ".offset5{margin-left:420px;}\n" \
+ ".offset4{margin-left:340px;}\n" \
+ ".offset3{margin-left:260px;}\n" \
+ ".offset2{margin-left:180px;}\n" \
+ ".offset1{margin-left:100px;}\n" \
+ ".row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:\"\";}" \
+ "\n" \
+ ".row-fluid:after{clear:both;}\n" \
+ ".row-fluid " \
+ "[class*=\"span\"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-" \
+ "box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:" \
+ "2.127659574%;*margin-left:2.0744680846382977%;}\n" \
+ ".row-fluid [class*=\"span\"]:first-child{margin-left:0;}\n" \
+ ".row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%;}\n" \
+ ".row-fluid .span11{width:91.489361693%;*width:91.4361702036383%;}\n" \
+ ".row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%;}\n" \
+ ".row-fluid .span9{width:74.468085099%;*width:74.4148936096383%;}\n" \
+ ".row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%;}\n" \
+ ".row-fluid .span7{width:57.446808505%;*width:57.3936170156383%;}\n" \
+ ".row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%;}\n" \
+ ".row-fluid .span5{width:40.425531911%;*width:40.3723404216383%;}\n" \
+ ".row-fluid .span4{width:31.914893614%;*width:31.8617021246383%;}\n" \
+ ".row-fluid .span3{width:23.404255317%;*width:23.3510638276383%;}\n" \
+ ".row-fluid .span2{width:14.89361702%;*width:14.8404255306383%;}\n" \
+ ".row-fluid .span1{width:6.382978723%;*width:6.329787233638298%;}\n" \
+ ".container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{" \
+ "display:table;content:\"\";}\n" \
+ ".container:after{clear:both;}\n" \
+ ".container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,." \
+ "container-fluid:after{display:table;content:\"\";}\n" \
+ ".container-fluid:after{clear:both;}\n" \
+ "p{margin:0 0 9px;font-family:\"Helvetica " \
+ "Neue\",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p " \
+ "small{font-size:11px;color:#999999;}\n" \
+ ".lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}\n" \
+ "h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:" \
+ "optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 " \
+ "small{font-weight:normal;color:#999999;}\n" \
+ "h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}\n" \
+ "h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}\n" \
+ "h3{font-size:18px;line-height:27px;}h3 small{font-size:14px;}\n" \
+ "h4,h5,h6{line-height:18px;}\n" \
+ "h4{font-size:14px;}h4 small{font-size:12px;}\n" \
+ "h5{font-size:12px;}\n" \
+ "h6{font-size:11px;color:#999999;text-transform:uppercase;}\n" \
+ ".page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}\n" \
+ ".page-header h1{line-height:1;}\n" \
+ "ul,ol{padding:0;margin:0 0 9px 25px;}\n" \
+ "ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}\n" \
+ "ul{list-style:disc;}\n" \
+ "ol{list-style:decimal;}\n" \
+ "li{line-height:18px;}\n" \
+ "ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}\n" \
+ "dl{margin-bottom:18px;}\n" \
+ "dt,dd{line-height:18px;}\n" \
+ "dt{font-weight:bold;line-height:17px;}\n" \
+ "dd{margin-left:9px;}\n" \
+ ".dl-horizontal " \
+ "dt{float:left;width:120px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;" \
+ "white-space:nowrap;}\n" \
+ ".dl-horizontal dd{margin-left:130px;}\n" \
+ "hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}\n" \
+ "strong{font-weight:bold;}\n" \
+ "em{font-style:italic;}\n" \
+ ".muted{color:#999999;}\n" \
+ "abbr[title]{cursor:help;border-bottom:1px dotted #ddd;}\n" \
+ "abbr.initialism{font-size:90%;text-transform:uppercase;}\n" \
+ "blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote " \
+ "p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}\n" \
+ "blockquote small{display:block;line-height:18px;color:#999999;}blockquote " \
+ "small:before{content:'\\2014 \\00A0';}\n" \
+ "blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid " \
+ "#eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right " \
+ "small{text-align:right;}\n" \
+ "q:before,q:after,blockquote:before,blockquote:after{content:\"\";}\n" \
+ "address{display:block;margin-bottom:18px;font-style:normal;line-height:18px;}\n" \
+ "small{font-size:100%;}\n" \
+ "cite{font-style:normal;}\n" \
+ "code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,\"Courier " \
+ "New\",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;" \
+ "border-radius:3px;}\n" \
+ "code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}\n" \
+ "pre{display:block;padding:8.5px;margin:0 0 " \
+ "9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:" \
+ "pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid " \
+ "rgba(0, 0, 0, " \
+ "0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{" \
+ "margin-bottom:18px;}\n" \
+ "pre code{padding:0;color:inherit;background-color:transparent;border:0;}\n" \
+ ".pre-scrollable{max-height:340px;overflow-y:scroll;}\n" \
+ ".label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#ffffff;vertical-" \
+ "align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, " \
+ "0.25);background-color:#999999;}\n" \
+ ".label{padding:1px 4px " \
+ "2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}\n" \
+ ".badge{padding:1px 9px " \
+ "2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}\n" \
+ "a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}\n" \
+ ".label-important,.badge-important{background-color:#b94a48;}\n" \
+ ".label-important[href],.badge-important[href]{background-color:#953b39;}\n" \
+ ".label-warning,.badge-warning{background-color:#f89406;}\n" \
+ ".label-warning[href],.badge-warning[href]{background-color:#c67605;}\n" \
+ ".label-success,.badge-success{background-color:#468847;}\n" \
+ ".label-success[href],.badge-success[href]{background-color:#356635;}\n" \
+ ".label-info,.badge-info{background-color:#3a87ad;}\n" \
+ ".label-info[href],.badge-info[href]{background-color:#2d6987;}\n" \
+ ".label-inverse,.badge-inverse{background-color:#333333;}\n" \
+ ".label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}\n" \
+ "table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}" \
+ "\n" \
+ ".table{width:100%;margin-bottom:18px;}.table th,.table " \
+ "td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid " \
+ "#dddddd;}\n" \
+ ".table th{font-weight:bold;}\n" \
+ ".table thead th{vertical-align:bottom;}\n" \
+ ".table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table " \
+ "colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table " \
+ "thead:first-child tr:first-child th,.table thead:first-child tr:first-child " \
+ "td{border-top:0;}\n" \
+ ".table tbody+tbody{border-top:2px solid #dddddd;}\n" \
+ ".table-condensed th,.table-condensed td{padding:4px 5px;}\n" \
+ ".table-bordered{border:1px solid " \
+ "#dddddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-" \
+ "radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered " \
+ "td{border-left:1px solid #dddddd;}\n" \
+ ".table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child " \
+ "th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead " \
+ "tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered " \
+ "colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child " \
+ "th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child " \
+ "tr:first-child td{border-top:0;}\n" \
+ ".table-bordered thead:first-child tr:first-child th:first-child,.table-bordered " \
+ "tbody:first-child tr:first-child " \
+ "td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-" \
+ "radius-topleft:4px;}\n" \
+ ".table-bordered thead:first-child tr:first-child th:last-child,.table-bordered " \
+ "tbody:first-child tr:first-child " \
+ "td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-" \
+ "radius-topright:4px;}\n" \
+ ".table-bordered thead:last-child tr:last-child th:first-child,.table-bordered " \
+ "tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 " \
+ "4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 " \
+ "4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-" \
+ "bottomleft:4px;}\n" \
+ ".table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child " \
+ "tr:last-child " \
+ "td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-" \
+ "border-radius-bottomright:4px;}\n" \
+ ".table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) " \
+ "th{background-color:#f9f9f9;}\n" \
+ ".table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}\n" \
+ "table .span1{float:none;width:44px;margin-left:0;}\n" \
+ "table .span2{float:none;width:124px;margin-left:0;}\n" \
+ "table .span3{float:none;width:204px;margin-left:0;}\n" \
+ "table .span4{float:none;width:284px;margin-left:0;}\n" \
+ "table .span5{float:none;width:364px;margin-left:0;}\n" \
+ "table .span6{float:none;width:444px;margin-left:0;}\n" \
+ "table .span7{float:none;width:524px;margin-left:0;}\n" \
+ "table .span8{float:none;width:604px;margin-left:0;}\n" \
+ "table .span9{float:none;width:684px;margin-left:0;}\n" \
+ "table .span10{float:none;width:764px;margin-left:0;}\n" \
+ "table .span11{float:none;width:844px;margin-left:0;}\n" \
+ "table .span12{float:none;width:924px;margin-left:0;}\n" \
+ "table .span13{float:none;width:1004px;margin-left:0;}\n" \
+ "table .span14{float:none;width:1084px;margin-left:0;}\n" \
+ "table .span15{float:none;width:1164px;margin-left:0;}\n" \
+ "table .span16{float:none;width:1244px;margin-left:0;}\n" \
+ "table .span17{float:none;width:1324px;margin-left:0;}\n" \
+ "table .span18{float:none;width:1404px;margin-left:0;}\n" \
+ "table .span19{float:none;width:1484px;margin-left:0;}\n" \
+ "table .span20{float:none;width:1564px;margin-left:0;}\n" \
+ "table .span21{float:none;width:1644px;margin-left:0;}\n" \
+ "table .span22{float:none;width:1724px;margin-left:0;}\n" \
+ "table .span23{float:none;width:1804px;margin-left:0;}\n" \
+ "table .span24{float:none;width:1884px;margin-left:0;}"
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc
new file mode 100644
index 000000000..7254e12b1
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc
@@ -0,0 +1,5456 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <cassert>
+#include <ctime>
+
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <vector>
+#include <cctype>
+
+#include <sys/stat.h>
+#include <stdexcept>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::setfill;
+using std::setw;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Java code generator.
+ *
+ */
+class t_java_generator : public t_oop_generator {
+public:
+ t_java_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ bean_style_ = false;
+ android_style_ = false;
+ private_members_ = false;
+ nocamel_style_ = false;
+ fullcamel_style_ = false;
+ android_legacy_ = false;
+ sorted_containers_ = false;
+ java5_ = false;
+ reuse_objects_ = false;
+ use_option_type_ = false;
+ undated_generated_annotations_ = false;
+ suppress_generated_annotations_ = false;
+ rethrow_unhandled_exceptions_ = false;
+ unsafe_binaries_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("beans") == 0) {
+ bean_style_ = true;
+ } else if( iter->first.compare("android") == 0) {
+ android_style_ = true;
+ } else if( iter->first.compare("private-members") == 0) {
+ private_members_ = true;
+ } else if( iter->first.compare("nocamel") == 0) {
+ nocamel_style_ = true;
+ } else if( iter->first.compare("fullcamel") == 0) {
+ fullcamel_style_ = true;
+ } else if( iter->first.compare("android_legacy") == 0) {
+ android_legacy_ = true;
+ } else if( iter->first.compare("sorted_containers") == 0) {
+ sorted_containers_ = true;
+ } else if( iter->first.compare("java5") == 0) {
+ java5_ = true;
+ } else if( iter->first.compare("reuse-objects") == 0) {
+ reuse_objects_ = true;
+ } else if( iter->first.compare("option_type") == 0) {
+ use_option_type_ = true;
+ } else if( iter->first.compare("rethrow_unhandled_exceptions") == 0) {
+ rethrow_unhandled_exceptions_ = true;
+ } else if( iter->first.compare("generated_annotations") == 0) {
+ if( iter->second.compare("undated") == 0) {
+ undated_generated_annotations_ = true;
+ } else if(iter->second.compare("suppress") == 0) {
+ suppress_generated_annotations_ = true;
+ } else {
+ throw "unknown option java:" + iter->first + "=" + iter->second;
+ }
+ } else if( iter->first.compare("unsafe_binaries") == 0) {
+ unsafe_binaries_ = true;
+ } else {
+ throw "unknown option java:" + iter->first;
+ }
+ }
+
+ if (java5_) {
+ android_legacy_ = true;
+ }
+
+ out_dir_base_ = (bean_style_ ? "gen-javabean" : "gen-java");
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_union(t_struct* tunion);
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ void print_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval = false);
+ std::string render_const_value(std::ostream& out, t_type* type, t_const_value* value);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_java_struct(t_struct* tstruct, bool is_exception);
+
+ void generate_java_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool in_class = false,
+ bool is_result = false);
+ void generate_java_struct_parcelable(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_equality(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_compare_to(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_java_validator(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_tostring(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_clear(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_write_object(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_read_object(std::ostream& out, t_struct* tstruct);
+ void generate_java_meta_data_map(std::ostream& out, t_struct* tstruct);
+ void generate_field_value_meta_data(std::ostream& out, t_type* type);
+ std::string get_java_type_string(t_type* type);
+ void generate_java_struct_field_by_id(ostream& out, t_struct* tstruct);
+ void generate_reflection_setters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_reflection_getters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct);
+ void generate_generic_isset_method(std::ostream& out, t_struct* tstruct);
+ void generate_java_bean_boilerplate(std::ostream& out, t_struct* tstruct);
+
+ void generate_function_helpers(t_function* tfunction);
+ std::string as_camel_case(std::string name, bool ucfirst = true);
+ std::string get_rpc_method_name(std::string name);
+ std::string get_cap_name(std::string name);
+ std::string generate_isset_check(t_field* field);
+ std::string generate_isset_check(std::string field);
+ void generate_isset_set(ostream& out, t_field* field, std::string prefix);
+ std::string isset_field_id(t_field* field);
+
+ void generate_service_interface(t_service* tservice);
+ void generate_service_async_interface(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_async_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_service_async_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+ void generate_process_async_function(t_service* tservice, t_function* tfunction);
+
+ void generate_java_union(t_struct* tstruct);
+ void generate_union_constructor(ostream& out, t_struct* tstruct);
+ void generate_union_getters_and_setters(ostream& out, t_struct* tstruct);
+ void generate_union_is_set_methods(ostream& out, t_struct* tstruct);
+ void generate_union_abstract_methods(ostream& out, t_struct* tstruct);
+ void generate_check_type(ostream& out, t_struct* tstruct);
+ void generate_standard_scheme_read_value(ostream& out, t_struct* tstruct);
+ void generate_standard_scheme_write_value(ostream& out, t_struct* tstruct);
+ void generate_tuple_scheme_read_value(ostream& out, t_struct* tstruct);
+ void generate_tuple_scheme_write_value(ostream& out, t_struct* tstruct);
+ void generate_get_field_desc(ostream& out, t_struct* tstruct);
+ void generate_get_struct_desc(ostream& out, t_struct* tstruct);
+ void generate_get_field_name(ostream& out, t_struct* tstruct);
+
+ void generate_union_comparisons(ostream& out, t_struct* tstruct);
+ void generate_union_hashcode(ostream& out, t_struct* tstruct);
+
+ void generate_scheme_map(ostream& out, t_struct* tstruct);
+ void generate_standard_writer(ostream& out, t_struct* tstruct, bool is_result);
+ void generate_standard_reader(ostream& out, t_struct* tstruct);
+ void generate_java_struct_standard_scheme(ostream& out, t_struct* tstruct, bool is_result);
+
+ void generate_java_struct_tuple_scheme(ostream& out, t_struct* tstruct);
+ void generate_java_struct_tuple_reader(ostream& out, t_struct* tstruct);
+ void generate_java_struct_tuple_writer(ostream& out, t_struct* tstruct);
+
+ void generate_java_scheme_lookup(ostream& out);
+
+ void generate_javax_generated_annotation(ostream& out);
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool has_metadata = true);
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out,
+ t_type* ttype,
+ std::string prefix = "",
+ bool has_metadata = true);
+
+ void generate_deserialize_set_element(std::ostream& out,
+ t_set* tset,
+ std::string prefix = "",
+ std::string obj = "",
+ bool has_metadata = true);
+
+ void generate_deserialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string prefix = "",
+ std::string obj = "",
+ bool has_metadata = true);
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "",
+ std::string obj = "",
+ bool has_metadata = true);
+
+ void generate_serialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool has_metadata = true);
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out,
+ t_type* ttype,
+ std::string prefix = "",
+ bool has_metadata = true);
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string iter,
+ std::string map,
+ bool has_metadata = true);
+
+ void generate_serialize_set_element(std::ostream& out,
+ t_set* tmap,
+ std::string iter,
+ bool has_metadata = true);
+
+ void generate_serialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string iter,
+ bool has_metadata = true);
+
+ void generate_deep_copy_container(std::ostream& out,
+ std::string source_name_p1,
+ std::string source_name_p2,
+ std::string result_name,
+ t_type* type);
+ void generate_deep_copy_non_container(std::ostream& out,
+ std::string source_name,
+ std::string dest_name,
+ t_type* type);
+
+ enum isset_type { ISSET_NONE, ISSET_PRIMITIVE, ISSET_BITSET };
+ isset_type needs_isset(t_struct* tstruct, std::string* outPrimitiveType = NULL);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string java_package();
+ std::string java_suppressions();
+ std::string java_nullable_annotation();
+ std::string type_name(t_type* ttype,
+ bool in_container = false,
+ bool in_init = false,
+ bool skip_generic = false,
+ bool force_namespace = false);
+ std::string base_type_name(t_base_type* tbase, bool in_container = false);
+ std::string declare_field(t_field* tfield, bool init = false, bool comment = false);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string function_signature_async(t_function* tfunction,
+ bool use_base_method = false,
+ std::string prefix = "");
+ std::string argument_list(t_struct* tstruct, bool include_types = true);
+ std::string async_function_call_arglist(t_function* tfunc,
+ bool use_base_method = true,
+ bool include_types = true);
+ std::string async_argument_list(t_function* tfunct,
+ t_struct* tstruct,
+ t_type* ttype,
+ bool include_types = false);
+ std::string type_to_enum(t_type* ttype);
+ void generate_struct_desc(ostream& out, t_struct* tstruct);
+ void generate_field_descs(ostream& out, t_struct* tstruct);
+ void generate_field_name_constants(ostream& out, t_struct* tstruct);
+
+ std::string make_valid_java_filename(std::string const& fromName);
+ std::string make_valid_java_identifier(std::string const& fromName);
+
+ bool type_can_be_null(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()
+ || ttype->is_enum();
+ }
+
+ bool is_deprecated(const std::map<std::string, std::string>& annotations) {
+ return annotations.find("deprecated") != annotations.end();
+ }
+
+ bool is_enum_set(t_type* ttype) {
+ if (!sorted_containers_) {
+ ttype = get_true_type(ttype);
+ if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ t_type* elem_type = get_true_type(tset->get_elem_type());
+ return elem_type->is_enum();
+ }
+ }
+ return false;
+ }
+
+ bool is_enum_map(t_type* ttype) {
+ if (!sorted_containers_) {
+ ttype = get_true_type(ttype);
+ if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ t_type* key_type = get_true_type(tmap->get_key_type());
+ return key_type->is_enum();
+ }
+ }
+ return false;
+ }
+
+ std::string inner_enum_type_name(t_type* ttype) {
+ ttype = get_true_type(ttype);
+ if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ t_type* key_type = get_true_type(tmap->get_key_type());
+ return type_name(key_type, true) + ".class";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ t_type* elem_type = get_true_type(tset->get_elem_type());
+ return type_name(elem_type, true) + ".class";
+ }
+ return "";
+ }
+
+ std::string constant_name(std::string name);
+
+private:
+ /**
+ * File streams
+ */
+
+ std::string package_name_;
+ ofstream_with_content_based_conditional_update f_service_;
+ std::string package_dir_;
+
+ bool bean_style_;
+ bool android_style_;
+ bool private_members_;
+ bool nocamel_style_;
+ bool fullcamel_style_;
+ bool android_legacy_;
+ bool java5_;
+ bool sorted_containers_;
+ bool reuse_objects_;
+ bool use_option_type_;
+ bool undated_generated_annotations_;
+ bool suppress_generated_annotations_;
+ bool rethrow_unhandled_exceptions_;
+ bool unsafe_binaries_;
+
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_java_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+ package_name_ = program_->get_namespace("java");
+
+ string dir = package_name_;
+ string subdir = get_out_dir();
+ string::size_type loc;
+ while ((loc = dir.find(".")) != string::npos) {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0) {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ package_dir_ = subdir;
+}
+
+/**
+ * Packages the generated file
+ *
+ * @return String of the package, i.e. "package org.apache.thriftdemo;"
+ */
+string t_java_generator::java_package() {
+ if (!package_name_.empty()) {
+ return string("package ") + package_name_ + ";\n\n";
+ }
+ return "";
+}
+
+string t_java_generator::java_suppressions() {
+ return "@SuppressWarnings({\"cast\", \"rawtypes\", \"serial\", \"unchecked\", \"unused\"})\n";
+}
+
+string t_java_generator::java_nullable_annotation() {
+ return "@org.apache.thrift.annotation.Nullable";
+}
+
+/**
+ * Nothing in Java
+ */
+void t_java_generator::close_generator() {
+}
+
+/**
+ * Generates a typedef. This is not done in Java, since it does
+ * not support arbitrary name replacements, and it'd be a wacky waste
+ * of overhead to make wrapper classes.
+ *
+ * @param ttypedef The type definition
+ */
+void t_java_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Enums are a class with a set of static constants.
+ *
+ * @param tenum The enumeration
+ */
+void t_java_generator::generate_enum(t_enum* tenum) {
+ bool is_deprecated = this->is_deprecated(tenum->annotations_);
+ // Make output file
+ string f_enum_name = package_dir_ + "/" + make_valid_java_filename(tenum->get_name()) + ".java";
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ // Comment and package it
+ f_enum << autogen_comment() << java_package() << endl;
+
+ generate_java_doc(f_enum, tenum);
+
+ if (!suppress_generated_annotations_) {
+ generate_javax_generated_annotation(f_enum);
+ }
+
+ if (is_deprecated) {
+ indent(f_enum) << "@Deprecated" << endl;
+ }
+ indent(f_enum) << "public enum " << tenum->get_name() << " implements org.apache.thrift.TEnum ";
+ scope_up(f_enum);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ bool first = true;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+
+ if (first) {
+ first = false;
+ } else {
+ f_enum << "," << endl;
+ }
+
+ generate_java_doc(f_enum, *c_iter);
+ if (this->is_deprecated((*c_iter)->annotations_)) {
+ indent(f_enum) << "@Deprecated" << endl;
+ }
+ indent(f_enum) << (*c_iter)->get_name() << "(" << value << ")";
+ }
+ f_enum << ";" << endl << endl;
+
+ // Field for thriftCode
+ indent(f_enum) << "private final int value;" << endl << endl;
+
+ indent(f_enum) << "private " << tenum->get_name() << "(int value) {" << endl;
+ indent(f_enum) << " this.value = value;" << endl;
+ indent(f_enum) << "}" << endl << endl;
+
+ indent(f_enum) << "/**" << endl;
+ indent(f_enum) << " * Get the integer value of this enum value, as defined in the Thrift IDL."
+ << endl;
+ indent(f_enum) << " */" << endl;
+ indent(f_enum) << "public int getValue() {" << endl;
+ indent(f_enum) << " return value;" << endl;
+ indent(f_enum) << "}" << endl << endl;
+
+ indent(f_enum) << "/**" << endl;
+ indent(f_enum) << " * Find a the enum type by its integer value, as defined in the Thrift IDL."
+ << endl;
+ indent(f_enum) << " * @return null if the value is not found." << endl;
+ indent(f_enum) << " */" << endl;
+ indent(f_enum) << java_nullable_annotation() << endl;
+ indent(f_enum) << "public static " + tenum->get_name() + " findByValue(int value) { " << endl;
+
+ indent_up();
+
+ indent(f_enum) << "switch (value) {" << endl;
+ indent_up();
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << "case " << value << ":" << endl;
+ indent(f_enum) << " return " << (*c_iter)->get_name() << ";" << endl;
+ }
+
+ indent(f_enum) << "default:" << endl;
+ indent(f_enum) << " return null;" << endl;
+
+ indent_down();
+
+ indent(f_enum) << "}" << endl;
+
+ indent_down();
+
+ indent(f_enum) << "}" << endl;
+
+ scope_down(f_enum);
+
+ f_enum.close();
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_java_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+
+ string f_consts_name = package_dir_ + '/' + make_valid_java_filename(program_name_)
+ + "Constants.java";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ // Print header
+ f_consts << autogen_comment() << java_package() << java_suppressions();
+
+ f_consts << "public class " << make_valid_java_identifier(program_name_) << "Constants {" << endl
+ << endl;
+ indent_up();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ generate_java_doc(f_consts, (*c_iter));
+ print_const_value(f_consts,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ false);
+ }
+ indent_down();
+ indent(f_consts) << "}" << endl;
+ f_consts.close();
+}
+
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+void t_java_generator::print_const_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval) {
+ type = get_true_type(type);
+
+ indent(out);
+ if (!defval) {
+ out << (in_static ? "" : "public static final ") << type_name(type) << " ";
+ }
+ if (type->is_base_type()) {
+ string v2 = render_const_value(out, type, value);
+ out << name << " = " << v2 << ";" << endl << endl;
+ } else if (type->is_enum()) {
+ out << name << " = " << render_const_value(out, type, value) << ";" << endl << endl;
+ } else if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& unsorted_fields = ((t_struct*)type)->get_members();
+ vector<t_field*> fields = unsorted_fields;
+ std::sort(fields.begin(), fields.end(), t_field::key_compare());
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ out << name << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "static {" << endl;
+ indent_up();
+ }
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(out, field_type, v_iter->second);
+ indent(out) << name << ".";
+ std::string cap_name = get_cap_name(v_iter->first->get_string());
+ out << "set" << cap_name << "(" << val << ");" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_map()) {
+ std::string constructor_args;
+ if (is_enum_map(type)) {
+ constructor_args = inner_enum_type_name(type);
+ }
+ out << name << " = new " << type_name(type, false, true) << "(" << constructor_args << ");" << endl;
+ if (!in_static) {
+ indent(out) << "static {" << endl;
+ indent_up();
+ }
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, ktype, v_iter->first);
+ string val = render_const_value(out, vtype, v_iter->second);
+ indent(out) << name << ".put(" << key << ", " << val << ");" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_list() || type->is_set()) {
+ if (is_enum_set(type)) {
+ out << name << " = " << type_name(type, false, true, true) << ".noneOf(" << inner_enum_type_name(type) << ");" << endl;
+ } else {
+ out << name << " = new " << type_name(type, false, true) << "();" << endl;
+ }
+ if (!in_static) {
+ indent(out) << "static {" << endl;
+ indent_up();
+ }
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, etype, *v_iter);
+ indent(out) << name << ".add(" << val << ");" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else {
+ throw "compiler error: no const of type " + type->get_name();
+ }
+}
+
+string t_java_generator::render_const_value(ostream& out, t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ if (((t_base_type*)type)->is_binary()) {
+ render << "java.nio.ByteBuffer.wrap(\"" << get_escaped_string(value) << "\".getBytes())";
+ } else {
+ render << '"' << get_escaped_string(value) << '"';
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ render << "(byte)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I16:
+ render << "(short)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ render << value->get_integer() << "L";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << value->get_integer() << "d";
+ } else {
+ render << emit_double_as_string(value->get_double());
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ std::string namespace_prefix = type->get_program()->get_namespace("java");
+ if (namespace_prefix.length() > 0) {
+ namespace_prefix += ".";
+ }
+ render << namespace_prefix << value->get_identifier_with_parent();
+ } else {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This will be a org.apache.thrift.TBase
+ * implementor.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_struct(t_struct* tstruct) {
+ if (tstruct->is_union()) {
+ generate_java_union(tstruct);
+ } else {
+ generate_java_struct(tstruct, false);
+ }
+}
+
+/**
+ * Exceptions are structs, but they inherit from Exception
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_xception(t_struct* txception) {
+ generate_java_struct(txception, true);
+}
+
+/**
+ * Java struct definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_struct(t_struct* tstruct, bool is_exception) {
+ // Make output file
+ string f_struct_name = package_dir_ + "/" + make_valid_java_filename(tstruct->get_name())
+ + ".java";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << java_package() << java_suppressions();
+
+ generate_java_struct_definition(f_struct, tstruct, is_exception);
+ f_struct.close();
+}
+
+/**
+ * Java union definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_union(t_struct* tstruct) {
+ // Make output file
+ string f_struct_name = package_dir_ + "/" + make_valid_java_filename(tstruct->get_name())
+ + ".java";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << java_package() << java_suppressions();
+
+ generate_java_doc(f_struct, tstruct);
+
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+ bool is_deprecated = this->is_deprecated(tstruct->annotations_);
+
+ if (!suppress_generated_annotations_) {
+ generate_javax_generated_annotation(f_struct);
+ }
+
+ if (is_deprecated) {
+ indent(f_struct) << "@Deprecated" << endl;
+ }
+ indent(f_struct) << "public " << (is_final ? "final " : "") << "class " << tstruct->get_name()
+ << " extends org.apache.thrift.TUnion<" << tstruct->get_name() << ", "
+ << tstruct->get_name() << "._Fields> ";
+
+ scope_up(f_struct);
+
+ generate_struct_desc(f_struct, tstruct);
+ generate_field_descs(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_field_name_constants(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_java_meta_data_map(f_struct, tstruct);
+
+ generate_union_constructor(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_abstract_methods(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_java_struct_field_by_id(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_getters_and_setters(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_is_set_methods(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_comparisons(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_hashcode(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_java_struct_write_object(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_java_struct_read_object(f_struct, tstruct);
+
+ f_struct << endl;
+
+ scope_down(f_struct);
+
+ f_struct.close();
+}
+
+void t_java_generator::generate_union_constructor(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ indent(out) << "public " << type_name(tstruct) << "() {" << endl;
+ indent_up();
+ bool default_value = false;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* type = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL) {
+ indent(out) << "super(_Fields." << constant_name((*m_iter)->get_name()) << ", "
+ << render_const_value(out, type, (*m_iter)->get_value()) << ");" << endl;
+ default_value = true;
+ break;
+ }
+ }
+ if (default_value == false) {
+ indent(out) << "super();" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public " << type_name(tstruct) << "(_Fields setField, java.lang.Object value) {" << endl;
+ indent(out) << " super(setField, value);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public " << type_name(tstruct) << "(" << type_name(tstruct) << " other) {"
+ << endl;
+ indent(out) << " super(other);" << endl;
+ indent(out) << "}" << endl;
+
+ indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl;
+ indent(out) << " return new " << tstruct->get_name() << "(this);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ // generate "constructors" for each field
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* type = (*m_iter)->get_type();
+ indent(out) << "public static " << type_name(tstruct) << " " << (*m_iter)->get_name() << "("
+ << type_name(type) << " value) {" << endl;
+ indent(out) << " " << type_name(tstruct) << " x = new " << type_name(tstruct) << "();" << endl;
+ indent(out) << " x.set" << get_cap_name((*m_iter)->get_name()) << "(value);" << endl;
+ indent(out) << " return x;" << endl;
+ indent(out) << "}" << endl << endl;
+
+ if (type->is_binary()) {
+ indent(out) << "public static " << type_name(tstruct) << " " << (*m_iter)->get_name()
+ << "(byte[] value) {" << endl;
+ indent(out) << " " << type_name(tstruct) << " x = new " << type_name(tstruct) << "();"
+ << endl;
+ indent(out) << " x.set" << get_cap_name((*m_iter)->get_name());
+ if(unsafe_binaries_) {
+ indent(out) << "(java.nio.ByteBuffer.wrap(value));" << endl;
+ }else{
+ indent(out) << "(java.nio.ByteBuffer.wrap(value.clone()));" << endl;
+ }
+ indent(out) << " return x;" << endl;
+ indent(out) << "}" << endl << endl;
+ }
+ }
+}
+
+void t_java_generator::generate_union_getters_and_setters(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << endl;
+ }
+
+ t_field* field = (*m_iter);
+ t_type* type = field->get_type();
+ std::string cap_name = get_cap_name(field->get_name());
+ bool is_deprecated = this->is_deprecated(field->annotations_);
+
+ generate_java_doc(out, field);
+ if (type->is_binary()) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public byte[] get" << cap_name << "() {" << endl;
+ indent(out) << " set" << cap_name << "(org.apache.thrift.TBaseHelper.rightSize(buffer"
+ << get_cap_name("for") << cap_name << "()));" << endl;
+ indent(out) << " java.nio.ByteBuffer b = buffer" << get_cap_name("for") << cap_name << "();" << endl;
+ indent(out) << " return b == null ? null : b.array();" << endl;
+ indent(out) << "}" << endl;
+
+ out << endl;
+
+ indent(out) << "public java.nio.ByteBuffer buffer" << get_cap_name("for")
+ << get_cap_name(field->get_name()) << "() {" << endl;
+ indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {"
+ << endl;
+
+ if(unsafe_binaries_){
+ indent(out)
+ << " return (java.nio.ByteBuffer)getFieldValue();"
+ << endl;
+ }else{
+ indent(out)
+ << " return org.apache.thrift.TBaseHelper.copyBinary((java.nio.ByteBuffer)getFieldValue());"
+ << endl;
+ }
+
+ indent(out) << " } else {" << endl;
+ indent(out) << " throw new java.lang.RuntimeException(\"Cannot get field '" << field->get_name()
+ << "' because union is currently set to \" + getFieldDesc(getSetField()).name);"
+ << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public " << type_name(field->get_type()) << " get"
+ << get_cap_name(field->get_name()) << "() {" << endl;
+ indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {"
+ << endl;
+ indent(out) << " return (" << type_name(field->get_type(), true) << ")getFieldValue();"
+ << endl;
+ indent(out) << " } else {" << endl;
+ indent(out) << " throw new java.lang.RuntimeException(\"Cannot get field '" << field->get_name()
+ << "' because union is currently set to \" + getFieldDesc(getSetField()).name);"
+ << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+ }
+
+ out << endl;
+
+ generate_java_doc(out, field);
+ if (type->is_binary()) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public void set" << get_cap_name(field->get_name()) << "(byte[] value) {"
+ << endl;
+ indent(out) << " set" << get_cap_name(field->get_name());
+
+ if(unsafe_binaries_){
+ indent(out) << "(java.nio.ByteBuffer.wrap(value));" << endl;
+ }else{
+ indent(out) << "(java.nio.ByteBuffer.wrap(value.clone()));" << endl;
+ }
+
+ indent(out) << "}" << endl;
+
+ out << endl;
+ }
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public void set" << get_cap_name(field->get_name()) << "("
+ << type_name(field->get_type()) << " value) {" << endl;
+ if (type_can_be_null(field->get_type())) {
+ indent(out) << " if (value == null) throw new java.lang.NullPointerException();" << endl;
+ }
+ indent(out) << " setField_ = _Fields." << constant_name(field->get_name()) << ";" << endl;
+ indent(out) << " value_ = value;" << endl;
+ indent(out) << "}" << endl;
+ }
+}
+
+void t_java_generator::generate_union_is_set_methods(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << endl;
+ }
+
+ std::string field_name = (*m_iter)->get_name();
+
+ indent(out) << "public boolean is" << get_cap_name("set") << get_cap_name(field_name) << "() {"
+ << endl;
+ indent_up();
+ indent(out) << "return setField_ == _Fields." << constant_name(field_name) << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+}
+
+void t_java_generator::generate_union_abstract_methods(ostream& out, t_struct* tstruct) {
+ generate_check_type(out, tstruct);
+ out << endl;
+ generate_standard_scheme_read_value(out, tstruct);
+ out << endl;
+ generate_standard_scheme_write_value(out, tstruct);
+ out << endl;
+ generate_tuple_scheme_read_value(out, tstruct);
+ out << endl;
+ generate_tuple_scheme_write_value(out, tstruct);
+ out << endl;
+ generate_get_field_desc(out, tstruct);
+ out << endl;
+ generate_get_struct_desc(out, tstruct);
+ out << endl;
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected _Fields enumForId(short id) {" << endl;
+ indent(out) << " return _Fields.findByThriftIdOrThrow(id);" << endl;
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_check_type(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out)
+ << "protected void checkType(_Fields setField, java.lang.Object value) throws java.lang.ClassCastException {"
+ << endl;
+ indent_up();
+
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent(out) << " if (value instanceof " << type_name(field->get_type(), true, false, true)
+ << ") {" << endl;
+ indent(out) << " break;" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << " throw new java.lang.ClassCastException(\"Was expecting value of type "
+ << type_name(field->get_type(), true, false) << " for field '" << field->get_name()
+ << "', but got \" + value.getClass().getSimpleName());" << endl;
+ // do the real check here
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new java.lang.IllegalArgumentException(\"Unknown field id \" + setField);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_standard_scheme_read_value(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected java.lang.Object standardSchemeReadValue(org.apache.thrift.protocol.TProtocol "
+ "iprot, org.apache.thrift.protocol.TField field) throws "
+ "org.apache.thrift.TException {" << endl;
+
+ indent_up();
+
+ indent(out) << "_Fields setField = _Fields.findByThriftId(field.id);" << endl;
+ indent(out) << "if (setField != null) {" << endl;
+ indent_up();
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << "if (field.type == " << constant_name(field->get_name()) << "_FIELD_DESC.type) {"
+ << endl;
+ indent_up();
+ indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << ";"
+ << endl;
+ generate_deserialize_field(out, field, "");
+ indent(out) << "return " << field->get_name() << ";" << endl;
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent(out) << " org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);" << endl;
+ indent(out) << " return null;" << endl;
+ indent(out) << "}" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new java.lang.IllegalStateException(\"setField wasn't null, but didn't match any "
+ "of the case statements!\");" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent_up();
+ indent(out) << "org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);" << endl;
+ indent(out) << "return null;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_standard_scheme_write_value(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected void standardSchemeWriteValue(org.apache.thrift.protocol.TProtocol "
+ "oprot) throws org.apache.thrift.TException {" << endl;
+
+ indent_up();
+
+ indent(out) << "switch (setField_) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << " = ("
+ << type_name(field->get_type(), true, false) << ")value_;" << endl;
+ generate_serialize_field(out, field, "");
+ indent(out) << "return;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new java.lang.IllegalStateException(\"Cannot write union with unknown field \" + "
+ "setField_);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_tuple_scheme_read_value(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected java.lang.Object tupleSchemeReadValue(org.apache.thrift.protocol.TProtocol "
+ "iprot, short fieldID) throws org.apache.thrift.TException {" << endl;
+
+ indent_up();
+
+ indent(out) << "_Fields setField = _Fields.findByThriftId(fieldID);" << endl;
+ indent(out) << "if (setField != null) {" << endl;
+ indent_up();
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << ";"
+ << endl;
+ generate_deserialize_field(out, field, "");
+ indent(out) << "return " << field->get_name() << ";" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new java.lang.IllegalStateException(\"setField wasn't null, but didn't match any "
+ "of the case statements!\");" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent_up();
+ indent(out) << "throw new org.apache.thrift.protocol.TProtocolException(\"Couldn't find a field with field id \" + fieldID);"
+ << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_tuple_scheme_write_value(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected void tupleSchemeWriteValue(org.apache.thrift.protocol.TProtocol oprot) "
+ "throws org.apache.thrift.TException {" << endl;
+
+ indent_up();
+
+ indent(out) << "switch (setField_) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << " = ("
+ << type_name(field->get_type(), true, false) << ")value_;" << endl;
+ generate_serialize_field(out, field, "");
+ indent(out) << "return;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new java.lang.IllegalStateException(\"Cannot write union with unknown field \" + "
+ "setField_);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_get_field_desc(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected org.apache.thrift.protocol.TField getFieldDesc(_Fields setField) {"
+ << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent(out) << " return " << constant_name(field->get_name()) << "_FIELD_DESC;" << endl;
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new java.lang.IllegalArgumentException(\"Unknown field id \" + setField);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_get_struct_desc(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "@Override" << endl;
+ indent(out) << "protected org.apache.thrift.protocol.TStruct getStructDesc() {" << endl;
+ indent(out) << " return STRUCT_DESC;" << endl;
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_union_comparisons(ostream& out, t_struct* tstruct) {
+ // equality
+ indent(out) << "public boolean equals(java.lang.Object other) {" << endl;
+ indent(out) << " if (other instanceof " << tstruct->get_name() << ") {" << endl;
+ indent(out) << " return equals((" << tstruct->get_name() << ")other);" << endl;
+ indent(out) << " } else {" << endl;
+ indent(out) << " return false;" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+
+ out << endl;
+
+ indent(out) << "public boolean equals(" << tstruct->get_name() << " other) {" << endl;
+ indent(out) << " return other != null && getSetField() == other.getSetField() && "
+ "getFieldValue().equals(other.getFieldValue());" << endl;
+ indent(out) << "}" << endl;
+ out << endl;
+
+ indent(out) << "@Override" << endl;
+ indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl;
+ indent(out) << " int lastComparison = org.apache.thrift.TBaseHelper.compareTo(getSetField(), "
+ "other.getSetField());" << endl;
+ indent(out) << " if (lastComparison == 0) {" << endl;
+ indent(out) << " return org.apache.thrift.TBaseHelper.compareTo(getFieldValue(), "
+ "other.getFieldValue());" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << " return lastComparison;" << endl;
+ indent(out) << "}" << endl;
+ out << endl;
+}
+
+void t_java_generator::generate_union_hashcode(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "@Override" << endl;
+ indent(out) << "public int hashCode() {" << endl;
+ indent(out) << " java.util.List<java.lang.Object> list = new java.util.ArrayList<java.lang.Object>();" << endl;
+ indent(out) << " list.add(this.getClass().getName());" << endl;
+ indent(out) << " org.apache.thrift.TFieldIdEnum setField = getSetField();" << endl;
+ indent(out) << " if (setField != null) {" << endl;
+ indent(out) << " list.add(setField.getThriftFieldId());" << endl;
+ indent(out) << " java.lang.Object value = getFieldValue();" << endl;
+ indent(out) << " if (value instanceof org.apache.thrift.TEnum) {" << endl;
+ indent(out) << " list.add(((org.apache.thrift.TEnum)getFieldValue()).getValue());" << endl;
+ indent(out) << " } else {" << endl;
+ indent(out) << " list.add(value);" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << " return list.hashCode();" << endl;
+ indent(out) << "}";
+}
+
+/**
+ * Java struct definition. This has various parameters, as it could be
+ * generated standalone or inside another class as a helper. If it
+ * is a helper than it is a static class.
+ *
+ * @param tstruct The struct definition
+ * @param is_exception Is this an exception?
+ * @param in_class If inside a class, needs to be static class
+ * @param is_result If this is a result it needs a different writer
+ */
+void t_java_generator::generate_java_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool in_class,
+ bool is_result) {
+ generate_java_doc(out, tstruct);
+
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+ bool is_deprecated = this->is_deprecated(tstruct->annotations_);
+
+ if (!in_class && !suppress_generated_annotations_) {
+ generate_javax_generated_annotation(out);
+ }
+
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public " << (is_final ? "final " : "") << (in_class ? "static " : "") << "class "
+ << tstruct->get_name() << " ";
+
+ if (is_exception) {
+ out << "extends org.apache.thrift.TException ";
+ }
+ out << "implements org.apache.thrift.TBase<" << tstruct->get_name() << ", " << tstruct->get_name()
+ << "._Fields>, java.io.Serializable, Cloneable, Comparable<" << tstruct->get_name() << ">";
+
+ if (android_style_) {
+ out << ", android.os.Parcelable";
+ }
+
+ out << " ";
+
+ scope_up(out);
+
+ generate_struct_desc(out, tstruct);
+
+ // Members are public for -java, private for -javabean
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << endl;
+
+ generate_field_descs(out, tstruct);
+
+ out << endl;
+
+ generate_scheme_map(out, tstruct);
+
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (bean_style_ || private_members_) {
+ indent(out) << "private ";
+ } else {
+ generate_java_doc(out, *m_iter);
+ indent(out) << "public ";
+ }
+ out << declare_field(*m_iter, false, true) << endl;
+ }
+
+ out << endl;
+
+ if (android_style_) {
+ generate_java_struct_parcelable(out, tstruct);
+ }
+
+ generate_field_name_constants(out, tstruct);
+
+ // isset data
+ if (members.size() > 0) {
+ out << endl;
+
+ indent(out) << "// isset id assignments" << endl;
+
+ int i = 0;
+ int optionals = 0;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() == t_field::T_OPTIONAL) {
+ optionals++;
+ }
+ if (!type_can_be_null((*m_iter)->get_type())) {
+ indent(out) << "private static final int " << isset_field_id(*m_iter) << " = " << i << ";"
+ << endl;
+ i++;
+ }
+ }
+
+ std::string primitiveType;
+ switch (needs_isset(tstruct, &primitiveType)) {
+ case ISSET_NONE:
+ break;
+ case ISSET_PRIMITIVE:
+ indent(out) << "private " << primitiveType << " __isset_bitfield = 0;" << endl;
+ break;
+ case ISSET_BITSET:
+ indent(out) << "private java.util.BitSet __isset_bit_vector = new java.util.BitSet(" << i << ");" << endl;
+ break;
+ }
+
+ if (optionals > 0) {
+ std::string output_string = "private static final _Fields optionals[] = {";
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() == t_field::T_OPTIONAL) {
+ output_string = output_string + "_Fields." + constant_name((*m_iter)->get_name()) + ",";
+ }
+ }
+ indent(out) << output_string.substr(0, output_string.length() - 1) << "};" << endl;
+ }
+ }
+
+ generate_java_meta_data_map(out, tstruct);
+
+ bool all_optional_members = true;
+
+ // Default constructor
+ indent(out) << "public " << tstruct->get_name() << "() {" << endl;
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL) {
+ print_const_value(out,
+ "this." + (*m_iter)->get_name(),
+ t,
+ (*m_iter)->get_value(),
+ true,
+ true);
+ }
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ all_optional_members = false;
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ if (!members.empty() && !all_optional_members) {
+ // Full constructor for all fields
+ indent(out) << "public " << tstruct->get_name() << "(" << endl;
+ indent_up();
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ if (!first) {
+ out << "," << endl;
+ }
+ first = false;
+ indent(out) << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name();
+ }
+ }
+ out << ")" << endl;
+ indent_down();
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "this();" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ t_type* type = get_true_type((*m_iter)->get_type());
+ if (type->is_binary()) {
+ if(unsafe_binaries_){
+ indent(out) << "this." << (*m_iter)->get_name()
+ << " = " << (*m_iter)->get_name()
+ << ";" << endl;
+ }else{
+ indent(out) << "this." << (*m_iter)->get_name()
+ << " = org.apache.thrift.TBaseHelper.copyBinary(" << (*m_iter)->get_name()
+ << ");" << endl;
+ }
+ } else {
+ indent(out) << "this." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << ";"
+ << endl;
+ }
+ generate_isset_set(out, (*m_iter), "");
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ // copy constructor
+ indent(out) << "/**" << endl;
+ indent(out) << " * Performs a deep copy on <i>other</i>." << endl;
+ indent(out) << " */" << endl;
+ indent(out) << "public " << tstruct->get_name() << "(" << tstruct->get_name() << " other) {"
+ << endl;
+ indent_up();
+
+ switch (needs_isset(tstruct)) {
+ case ISSET_NONE:
+ break;
+ case ISSET_PRIMITIVE:
+ indent(out) << "__isset_bitfield = other.__isset_bitfield;" << endl;
+ break;
+ case ISSET_BITSET:
+ indent(out) << "__isset_bit_vector.clear();" << endl;
+ indent(out) << "__isset_bit_vector.or(other.__isset_bit_vector);" << endl;
+ break;
+ }
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+ std::string field_name = field->get_name();
+ t_type* type = field->get_type()->get_true_type();
+ bool can_be_null = type_can_be_null(type);
+
+ if (can_be_null) {
+ indent(out) << "if (other." << generate_isset_check(field) << ") {" << endl;
+ indent_up();
+ }
+
+ if (type->is_container()) {
+ generate_deep_copy_container(out, "other", field_name, "__this__" + field_name, type);
+ indent(out) << "this." << field_name << " = __this__" << field_name << ";" << endl;
+ } else {
+ indent(out) << "this." << field_name << " = ";
+ generate_deep_copy_non_container(out, "other." + field_name, field_name, type);
+ out << ";" << endl;
+ }
+
+ if (can_be_null) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // clone method, so that you can deep copy an object when you don't know its class.
+ indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl;
+ indent(out) << " return new " << tstruct->get_name() << "(this);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ generate_java_struct_clear(out, tstruct);
+
+ generate_java_bean_boilerplate(out, tstruct);
+ generate_generic_field_getters_setters(out, tstruct);
+ generate_generic_isset_method(out, tstruct);
+
+ generate_java_struct_equality(out, tstruct);
+ generate_java_struct_compare_to(out, tstruct);
+ generate_java_struct_field_by_id(out, tstruct);
+
+ generate_java_struct_reader(out, tstruct);
+ if (is_result) {
+ generate_java_struct_result_writer(out, tstruct);
+ } else {
+ generate_java_struct_writer(out, tstruct);
+ }
+ generate_java_struct_tostring(out, tstruct);
+ generate_java_validator(out, tstruct);
+
+ generate_java_struct_write_object(out, tstruct);
+ generate_java_struct_read_object(out, tstruct);
+
+ generate_java_struct_standard_scheme(out, tstruct, is_result);
+ generate_java_struct_tuple_scheme(out, tstruct);
+ generate_java_scheme_lookup(out);
+
+ scope_down(out);
+ out << endl;
+}
+
+/**
+ * generates parcelable interface implementation
+ */
+void t_java_generator::generate_java_struct_parcelable(ostream& out, t_struct* tstruct) {
+ string tname = tstruct->get_name();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << indent() << "@Override" << endl << indent()
+ << "public void writeToParcel(android.os.Parcel out, int flags) {" << endl;
+ indent_up();
+ string bitsetPrimitiveType = "";
+ switch (needs_isset(tstruct, &bitsetPrimitiveType)) {
+ case ISSET_NONE:
+ break;
+ case ISSET_PRIMITIVE:
+ indent(out) << "//primitive bitfield of type: " << bitsetPrimitiveType << endl;
+ if (bitsetPrimitiveType == "byte") {
+ indent(out) << "out.writeByte(__isset_bitfield);" << endl;
+ } else if (bitsetPrimitiveType == "short") {
+ indent(out) << "out.writeInt(new Short(__isset_bitfield).intValue());" << endl;
+ } else if (bitsetPrimitiveType == "int") {
+ indent(out) << "out.writeInt(__isset_bitfield);" << endl;
+ } else if (bitsetPrimitiveType == "long") {
+ indent(out) << "out.writeLong(__isset_bitfield);" << endl;
+ }
+ out << endl;
+ break;
+ case ISSET_BITSET:
+ indent(out) << "//BitSet" << endl;
+ indent(out) << "out.writeSerializable(__isset_bit_vector);" << endl;
+ out << endl;
+ break;
+ }
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ string name = (*m_iter)->get_name();
+
+ if (t->is_struct()) {
+ indent(out) << "out.writeParcelable(" << name << ", flags);" << endl;
+ } else if (type_name(t) == "float") {
+ indent(out) << "out.writeFloat(" << name << ");" << endl;
+ } else if (t->is_enum()) {
+ indent(out) << "out.writeInt(" << name << " != null ? " << name << ".getValue() : -1);" << endl;
+ } else if (t->is_list()) {
+ if (((t_list*)t)->get_elem_type()->get_true_type()->is_struct()) {
+ indent(out) << "out.writeTypedList(" << name << ");" << endl;
+ } else {
+ indent(out) << "out.writeList(" << name << ");" << endl;
+ }
+ } else if (t->is_map()) {
+ indent(out) << "out.writeMap(" << name << ");" << endl;
+ } else if (t->is_base_type()) {
+ if (t->is_binary()) {
+ indent(out) << "out.writeInt(" << name << "!=null ? 1 : 0);" << endl;
+ indent(out) << "if(" << name << " != null) { " << endl;
+ indent_up();
+ indent(out) << "out.writeByteArray(" << name << ".array(), " << name << ".position() + "
+ << name << ".arrayOffset(), " << name << ".limit() - " << name
+ << ".position() );" << endl;
+ scope_down(out);
+ } else {
+ switch (((t_base_type*)t)->get_base()) {
+ case t_base_type::TYPE_I16:
+ indent(out) << "out.writeInt(new Short(" << name << ").intValue());" << endl;
+ break;
+ case t_base_type::TYPE_I32:
+ indent(out) << "out.writeInt(" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_I64:
+ indent(out) << "out.writeLong(" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ indent(out) << "out.writeInt(" << name << " ? 1 : 0);" << endl;
+ break;
+ case t_base_type::TYPE_I8:
+ indent(out) << "out.writeByte(" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ indent(out) << "out.writeDouble(" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_STRING:
+ indent(out) << "out.writeString(" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_VOID:
+ break;
+ }
+ }
+ }
+ }
+ scope_down(out);
+ out << endl;
+
+ out << indent() << "@Override" << endl << indent() << "public int describeContents() {" << endl;
+ indent_up();
+ out << indent() << "return 0;" << endl;
+ scope_down(out);
+ out << endl;
+
+ indent(out) << "public " << tname << "(android.os.Parcel in) {" << endl;
+ indent_up();
+ // read in the required bitfield
+ switch (needs_isset(tstruct, &bitsetPrimitiveType)) {
+ case ISSET_NONE:
+ break;
+ case ISSET_PRIMITIVE:
+ indent(out) << "//primitive bitfield of type: " << bitsetPrimitiveType << endl;
+ if (bitsetPrimitiveType == "byte") {
+ indent(out) << "__isset_bitfield = in.readByte();" << endl;
+ } else if (bitsetPrimitiveType == "short") {
+ indent(out) << "__isset_bitfield = (short) in.readInt();" << endl;
+ } else if (bitsetPrimitiveType == "int") {
+ indent(out) << "__isset_bitfield = in.readInt();" << endl;
+ } else if (bitsetPrimitiveType == "long") {
+ indent(out) << "__isset_bitfield = in.readLong();" << endl;
+ }
+ out << endl;
+ break;
+ case ISSET_BITSET:
+ indent(out) << "//BitSet" << endl;
+ indent(out) << "__isset_bit_vector = (java.util.BitSet) in.readSerializable();" << endl;
+ out << endl;
+ break;
+ }
+ // read all the fields
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ string name = (*m_iter)->get_name();
+ string prefix = "this." + name;
+
+ if (t->is_struct()) {
+ indent(out) << prefix << "= in.readParcelable(" << tname << ".class.getClassLoader());"
+ << endl;
+ } else if (t->is_enum()) {
+ indent(out) << prefix << " = " << type_name(t) << ".findByValue(in.readInt());" << endl;
+ } else if (t->is_list()) {
+ t_list* list = (t_list*)t;
+ indent(out) << prefix << " = new " << type_name(t, false, true) << "();" << endl;
+ if (list->get_elem_type()->get_true_type()->is_struct()) {
+ indent(out) << "in.readTypedList(" << prefix << ", " << type_name(list->get_elem_type())
+ << ".CREATOR);" << endl;
+ } else {
+ indent(out) << "in.readList(" << prefix << ", " << tname << ".class.getClassLoader());"
+ << endl;
+ }
+ } else if (t->is_map()) {
+ indent(out) << prefix << " = new " << type_name(t, false, true) << "();" << endl;
+ indent(out) << " in.readMap(" << prefix << ", " << tname << ".class.getClassLoader());"
+ << endl;
+ } else if (type_name(t) == "float") {
+ indent(out) << prefix << " = in.readFloat();" << endl;
+ } else if (t->is_base_type()) {
+ t_base_type* bt = (t_base_type*)t;
+ if (bt->is_binary()) {
+ indent(out) << "if(in.readInt()==1) {" << endl;
+ indent_up();
+ indent(out) << prefix << " = java.nio.ByteBuffer.wrap(in.createByteArray());" << endl;
+ scope_down(out);
+ } else {
+ switch (bt->get_base()) {
+ case t_base_type::TYPE_I16:
+ indent(out) << prefix << " = (short) in.readInt();" << endl;
+ break;
+ case t_base_type::TYPE_I32:
+ indent(out) << prefix << " = in.readInt();" << endl;
+ break;
+ case t_base_type::TYPE_I64:
+ indent(out) << prefix << " = in.readLong();" << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ indent(out) << prefix << " = (in.readInt()==1);" << endl;
+ break;
+ case t_base_type::TYPE_I8:
+ indent(out) << prefix << " = in.readByte();" << endl;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ indent(out) << prefix << " = in.readDouble();" << endl;
+ break;
+ case t_base_type::TYPE_STRING:
+ indent(out) << prefix << "= in.readString();" << endl;
+ break;
+ case t_base_type::TYPE_VOID:
+ break;
+ }
+ }
+ }
+ }
+
+ scope_down(out);
+ out << endl;
+
+ indent(out) << "public static final android.os.Parcelable.Creator<" << tname
+ << "> CREATOR = new android.os.Parcelable.Creator<" << tname << ">() {" << endl;
+ indent_up();
+
+ indent(out) << "@Override" << endl << indent() << "public " << tname << "[] newArray(int size) {"
+ << endl;
+ indent_up();
+ indent(out) << "return new " << tname << "[size];" << endl;
+ scope_down(out);
+ out << endl;
+
+ indent(out) << "@Override" << endl << indent() << "public " << tname
+ << " createFromParcel(android.os.Parcel in) {" << endl;
+ indent_up();
+ indent(out) << "return new " << tname << "(in);" << endl;
+ scope_down(out);
+
+ indent_down();
+ indent(out) << "};" << endl;
+ out << endl;
+}
+
+/**
+ * Generates equals methods and a hashCode method for a structure.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_struct_equality(ostream& out, t_struct* tstruct) {
+ out << indent() << "@Override" << endl << indent() << "public boolean equals(java.lang.Object that) {"
+ << endl;
+ indent_up();
+ out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl
+ << indent() << "if (that instanceof " << tstruct->get_name() << ")" << endl << indent()
+ << " return this.equals((" << tstruct->get_name() << ")that);" << endl << indent()
+ << "return false;" << endl;
+ scope_down(out);
+ out << endl;
+
+ out << indent() << "public boolean equals(" << tstruct->get_name() << " that) {" << endl;
+ indent_up();
+ out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl
+ << indent() << "if (this == that)" << endl << indent() << " return true;" << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << endl;
+
+ t_type* t = get_true_type((*m_iter)->get_type());
+ // Most existing Thrift code does not use isset or optional/required,
+ // so we treat "default" fields as required.
+ bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
+ bool can_be_null = type_can_be_null(t);
+ string name = (*m_iter)->get_name();
+
+ string this_present = "true";
+ string that_present = "true";
+ string unequal;
+
+ if (is_optional || can_be_null) {
+ this_present += " && this." + generate_isset_check(*m_iter);
+ that_present += " && that." + generate_isset_check(*m_iter);
+ }
+
+ out << indent() << "boolean this_present_" << name << " = " << this_present << ";" << endl
+ << indent() << "boolean that_present_" << name << " = " << that_present << ";" << endl
+ << indent() << "if ("
+ << "this_present_" << name << " || that_present_" << name << ") {" << endl;
+ indent_up();
+ out << indent() << "if (!("
+ << "this_present_" << name << " && that_present_" << name << "))" << endl << indent()
+ << " return false;" << endl;
+
+ if (t->is_binary()) {
+ unequal = "!this." + name + ".equals(that." + name + ")";
+ } else if (can_be_null) {
+ unequal = "!this." + name + ".equals(that." + name + ")";
+ } else {
+ unequal = "this." + name + " != that." + name;
+ }
+
+ out << indent() << "if (" << unequal << ")" << endl << indent() << " return false;" << endl;
+
+ scope_down(out);
+ }
+ out << endl;
+ indent(out) << "return true;" << endl;
+ scope_down(out);
+ out << endl;
+
+ const int MUL = 8191; // HashCode multiplier
+ const int B_YES = 131071;
+ const int B_NO = 524287;
+ out << indent() << "@Override" << endl << indent() << "public int hashCode() {" << endl;
+ indent_up();
+ indent(out) << "int hashCode = 1;" << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << endl;
+
+ t_type* t = get_true_type((*m_iter)->get_type());
+ bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
+ bool can_be_null = type_can_be_null(t);
+ string name = (*m_iter)->get_name();
+
+ if (is_optional || can_be_null) {
+ indent(out) << "hashCode = hashCode * " << MUL << " + ((" << generate_isset_check(*m_iter)
+ << ") ? " << B_YES << " : " << B_NO << ");" << endl;
+ }
+
+ if (is_optional || can_be_null) {
+ indent(out) << "if (" + generate_isset_check(*m_iter) + ")" << endl;
+ indent_up();
+ }
+
+ if (t->is_enum()) {
+ indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".getValue();" << endl;
+ } else if (t->is_base_type()) {
+ switch(((t_base_type*)t)->get_base()) {
+ case t_base_type::TYPE_STRING:
+ indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".hashCode();" << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ indent(out) << "hashCode = hashCode * " << MUL << " + ((" << name << ") ? "
+ << B_YES << " : " << B_NO << ");" << endl;
+ break;
+ case t_base_type::TYPE_I8:
+ indent(out) << "hashCode = hashCode * " << MUL << " + (int) (" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ";" << endl;
+ break;
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ indent(out) << "hashCode = hashCode * " << MUL << " + org.apache.thrift.TBaseHelper.hashCode(" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_VOID:
+ throw std::logic_error("compiler error: a struct field cannot be void");
+ default:
+ throw std::logic_error("compiler error: the following base type has no hashcode generator: " +
+ t_base_type::t_base_name(((t_base_type*)t)->get_base()));
+ }
+ } else {
+ indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".hashCode();" << endl;
+ }
+
+ if (is_optional || can_be_null) {
+ indent_down();
+ }
+ }
+
+ out << endl;
+ indent(out) << "return hashCode;" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_java_generator::generate_java_struct_compare_to(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl;
+ indent_up();
+
+ indent(out) << "if (!getClass().equals(other.getClass())) {" << endl;
+ indent(out) << " return getClass().getName().compareTo(other.getClass().getName());" << endl;
+ indent(out) << "}" << endl;
+ out << endl;
+
+ indent(out) << "int lastComparison = 0;" << endl;
+ out << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = *m_iter;
+ indent(out) << "lastComparison = java.lang.Boolean.valueOf(" << generate_isset_check(field)
+ << ").compareTo(other." << generate_isset_check(field) << ");" << endl;
+ indent(out) << "if (lastComparison != 0) {" << endl;
+ indent(out) << " return lastComparison;" << endl;
+ indent(out) << "}" << endl;
+
+ indent(out) << "if (" << generate_isset_check(field) << ") {" << endl;
+ indent(out) << " lastComparison = org.apache.thrift.TBaseHelper.compareTo(this."
+ << field->get_name() << ", other." << field->get_name() << ");" << endl;
+ indent(out) << " if (lastComparison != 0) {" << endl;
+ indent(out) << " return lastComparison;" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+ }
+
+ indent(out) << "return 0;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to read all the fields of the struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_struct_reader(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "public void read(org.apache.thrift.protocol.TProtocol iprot) throws "
+ "org.apache.thrift.TException {" << endl;
+ indent_up();
+ indent(out) << "scheme(iprot).read(iprot, this);" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+// generates java method to perform various checks
+// (e.g. check that all required fields are set)
+void t_java_generator::generate_java_validator(ostream& out, t_struct* tstruct) {
+ indent(out) << "public void validate() throws org.apache.thrift.TException {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "// check for required fields" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ if (bean_style_) {
+ out << indent() << "if (!" << generate_isset_check(*f_iter) << ") {" << endl << indent()
+ << " throw new org.apache.thrift.protocol.TProtocolException(\"Required field '"
+ << (*f_iter)->get_name() << "' is unset! Struct:\" + toString());" << endl << indent()
+ << "}" << endl << endl;
+ } else {
+ if (type_can_be_null((*f_iter)->get_type())) {
+ indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out)
+ << " throw new org.apache.thrift.protocol.TProtocolException(\"Required field '"
+ << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name()
+ << "' because it's a primitive and you chose the non-beans generator."
+ << endl;
+ }
+ }
+ }
+ }
+
+ out << indent() << "// check for sub-struct validity" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_type* type = (*f_iter)->get_type();
+ if (type->is_struct() && !((t_struct*)type)->is_union()) {
+ out << indent() << "if (" << (*f_iter)->get_name() << " != null) {" << endl;
+ out << indent() << " " << (*f_iter)->get_name() << ".validate();" << endl;
+ out << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_struct_writer(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "public void write(org.apache.thrift.protocol.TProtocol oprot) throws "
+ "org.apache.thrift.TException {" << endl;
+ indent_up();
+ indent(out) << "scheme(oprot).write(oprot, this);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct,
+ * which is a function result. These fields are only written
+ * if they are set in the Isset array, and only one of them
+ * can be set at a time.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_struct_result_writer(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "public void write(org.apache.thrift.protocol.TProtocol oprot) throws "
+ "org.apache.thrift.TException {" << endl;
+ indent_up();
+ indent(out) << "scheme(oprot).write(oprot, this);" << endl;
+
+ indent_down();
+ indent(out) << " }" << endl << endl;
+}
+
+void t_java_generator::generate_java_struct_field_by_id(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << java_nullable_annotation() << endl;
+ indent(out) << "public _Fields fieldForId(int fieldId) {" << endl;
+ indent(out) << " return _Fields.findByThriftId(fieldId);" << endl;
+ indent(out) << "}" << endl << endl;
+}
+
+void t_java_generator::generate_reflection_getters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ indent(out) << "case " << constant_name(field_name) << ":" << endl;
+ indent_up();
+ indent(out) << "return " << (type->is_bool() ? "is" : "get") << cap_name << "();" << endl << endl;
+ indent_down();
+}
+
+void t_java_generator::generate_reflection_setters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ const bool is_binary = type->is_binary();
+ indent(out) << "case " << constant_name(field_name) << ":" << endl;
+ indent_up();
+ indent(out) << "if (value == null) {" << endl;
+ indent(out) << " unset" << get_cap_name(field_name) << "();" << endl;
+ indent(out) << "} else {" << endl;
+ if (is_binary) {
+ indent_up();
+ indent(out) << "if (value instanceof byte[]) {" << endl;
+ indent(out) << " set" << cap_name << "((byte[])value);" << endl;
+ indent(out) << "} else {" << endl;
+ }
+ indent(out) << " set" << cap_name << "((" << type_name(type, true, false) << ")value);" << endl;
+ if (is_binary) {
+ indent(out) << "}" << endl;
+ indent_down();
+ }
+ indent(out) << "}" << endl;
+ indent(out) << "break;" << endl << endl;
+
+ indent_down();
+}
+
+void t_java_generator::generate_generic_field_getters_setters(std::ostream& out,
+ t_struct* tstruct) {
+ std::ostringstream getter_stream;
+ std::ostringstream setter_stream;
+
+ // build up the bodies of both the getter and setter at once
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ indent_up();
+ generate_reflection_setters(setter_stream, type, field_name, cap_name);
+ generate_reflection_getters(getter_stream, type, field_name, cap_name);
+ indent_down();
+ }
+
+ // create the setter
+
+ indent(out) << "public void setFieldValue(_Fields field, " << java_nullable_annotation()
+ << " java.lang.Object value) {" << endl;
+ indent(out) << " switch (field) {" << endl;
+ out << setter_stream.str();
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl << endl;
+
+ // create the getter
+ indent(out) << java_nullable_annotation() << endl;
+ indent(out) << "public java.lang.Object getFieldValue(_Fields field) {" << endl;
+ indent_up();
+ indent(out) << "switch (field) {" << endl;
+ out << getter_stream.str();
+ indent(out) << "}" << endl;
+ indent(out) << "throw new java.lang.IllegalStateException();" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+// Creates a generic isSet method that takes the field number as argument
+void t_java_generator::generate_generic_isset_method(std::ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // create the isSet method
+ indent(out) << "/** Returns true if field corresponding to fieldID is set (has been assigned a "
+ "value) and false otherwise */" << endl;
+ indent(out) << "public boolean isSet(_Fields field) {" << endl;
+ indent_up();
+ indent(out) << "if (field == null) {" << endl;
+ indent(out) << " throw new java.lang.IllegalArgumentException();" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "switch (field) {" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << "return " << generate_isset_check(field) << ";" << endl;
+ indent_down();
+ }
+
+ indent(out) << "}" << endl;
+ indent(out) << "throw new java.lang.IllegalStateException();" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a set of Java Bean boilerplate functions (setters, getters, etc.)
+ * for the given struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_bean_boilerplate(ostream& out, t_struct* tstruct) {
+ isset_type issetType = needs_isset(tstruct);
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+ bool optional = use_option_type_ && field->get_req() == t_field::T_OPTIONAL;
+ bool is_deprecated = this->is_deprecated(field->annotations_);
+
+ if (type->is_container()) {
+ // Method to return the size of the collection
+ if (optional) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public org.apache.thrift.Option<Integer> get" << cap_name;
+ out << get_cap_name("size() {") << endl;
+
+ indent_up();
+ indent(out) << "if (this." << field_name << " == null) {" << endl;
+ indent_up();
+ indent(out) << "return org.apache.thrift.Option.none();" << endl;
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent_up();
+ indent(out) << "return org.apache.thrift.Option.some(this." << field_name << ".size());" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ } else {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public int get" << cap_name;
+ out << get_cap_name("size() {") << endl;
+
+ indent_up();
+ indent(out) << "return (this." << field_name << " == null) ? 0 : "
+ << "this." << field_name << ".size();" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+ }
+
+ if (type->is_set() || type->is_list()) {
+ t_type* element_type;
+ if (type->is_set()) {
+ element_type = ((t_set*)type)->get_elem_type();
+ } else {
+ element_type = ((t_list*)type)->get_elem_type();
+ }
+
+ // Iterator getter for sets and lists
+ if (optional) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public org.apache.thrift.Option<java.util.Iterator<" << type_name(element_type, true, false)
+ << ">> get" << cap_name;
+ out << get_cap_name("iterator() {") << endl;
+
+ indent_up();
+ indent(out) << "if (this." << field_name << " == null) {" << endl;
+ indent_up();
+ indent(out) << "return org.apache.thrift.Option.none();" << endl;
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent_up();
+ indent(out) << "return org.apache.thrift.Option.some(this." << field_name << ".iterator());" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ } else {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << java_nullable_annotation() << endl;
+ indent(out) << "public java.util.Iterator<" << type_name(element_type, true, false)
+ << "> get" << cap_name;
+ out << get_cap_name("iterator() {") << endl;
+
+ indent_up();
+ indent(out) << "return (this." << field_name << " == null) ? null : "
+ << "this." << field_name << ".iterator();" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ // Add to set or list, create if the set/list is null
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public void add" << get_cap_name("to");
+ out << cap_name << "(" << type_name(element_type) << " elem) {" << endl;
+
+ indent_up();
+ indent(out) << "if (this." << field_name << " == null) {" << endl;
+ indent_up();
+ indent(out) << "this." << field_name;
+ if (is_enum_set(type)) {
+ out << " = " << type_name(type, false, true, true) << ".noneOf(" << inner_enum_type_name(type) << ");" << endl;
+ } else {
+ out << " = new " << type_name(type, false, true) << "();" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl;
+ indent(out) << "this." << field_name << ".add(elem);" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ } else if (type->is_map()) {
+ // Put to map
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public void put" << get_cap_name("to");
+ out << cap_name << "(" << type_name(key_type) << " key, " << type_name(val_type) << " val) {"
+ << endl;
+
+ indent_up();
+ indent(out) << "if (this." << field_name << " == null) {" << endl;
+ indent_up();
+ std::string constructor_args;
+ if (is_enum_map(type)) {
+ constructor_args = inner_enum_type_name(type);
+ }
+ indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "(" << constructor_args << ");"
+ << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent(out) << "this." << field_name << ".put(key, val);" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ // Simple getter
+ generate_java_doc(out, field);
+ if (type->is_binary()) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public byte[] get" << cap_name << "() {" << endl;
+ indent(out) << " set" << cap_name << "(org.apache.thrift.TBaseHelper.rightSize("
+ << field_name << "));" << endl;
+ indent(out) << " return " << field_name << " == null ? null : " << field_name << ".array();"
+ << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public java.nio.ByteBuffer buffer" << get_cap_name("for") << cap_name << "() {"
+ << endl;
+ if(unsafe_binaries_){
+ indent(out) << " return " << field_name << ";"
+ << endl;
+ }else {
+ indent(out) << " return org.apache.thrift.TBaseHelper.copyBinary(" << field_name << ");"
+ << endl;
+ }
+ indent(out) << "}" << endl << endl;
+ } else {
+ if (optional) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public org.apache.thrift.Option<" << type_name(type, true) << ">";
+ if (type->is_base_type() && ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) {
+ out << " is";
+ } else {
+ out << " get";
+ }
+ out << cap_name << "() {" << endl;
+ indent_up();
+
+ indent(out) << "if (this.isSet" << cap_name << "()) {" << endl;
+ indent_up();
+ indent(out) << "return org.apache.thrift.Option.some(this." << field_name << ");" << endl;
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent_up();
+ indent(out) << "return org.apache.thrift.Option.none();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ } else {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ if (type_can_be_null(type)) {
+ indent(out) << java_nullable_annotation() << endl;
+ }
+ indent(out) << "public " << type_name(type);
+ if (type->is_base_type() && ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) {
+ out << " is";
+ } else {
+ out << " get";
+ }
+ out << cap_name << "() {" << endl;
+ indent_up();
+ indent(out) << "return this." << field_name << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+ }
+
+ // Simple setter
+ generate_java_doc(out, field);
+ if (type->is_binary()) {
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public ";
+ if (bean_style_) {
+ out << "void";
+ } else {
+ out << type_name(tstruct);
+ }
+ out << " set" << cap_name << "(byte[] " << field_name << ") {" << endl;
+ indent(out) << " this." << field_name << " = " << field_name << " == null ? (java.nio.ByteBuffer)null";
+
+ if(unsafe_binaries_){
+ indent(out) << " : java.nio.ByteBuffer.wrap(" << field_name << ");" << endl;
+ }else{
+ indent(out) << " : java.nio.ByteBuffer.wrap(" << field_name << ".clone());" << endl;
+ }
+
+ if (!bean_style_) {
+ indent(out) << " return this;" << endl;
+ }
+ indent(out) << "}" << endl << endl;
+ }
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public ";
+ if (bean_style_) {
+ out << "void";
+ } else {
+ out << type_name(tstruct);
+ }
+ out << " set" << cap_name << "(" << (type_can_be_null(type) ? (java_nullable_annotation() + " ") : "")
+ << type_name(type) << " " << field_name << ") {" << endl;
+ indent_up();
+ indent(out) << "this." << field_name << " = ";
+ if (type->is_binary() && !unsafe_binaries_) {
+ out << "org.apache.thrift.TBaseHelper.copyBinary(" << field_name << ")";
+ } else {
+ out << field_name;
+ }
+ out << ";" << endl;
+ generate_isset_set(out, field, "");
+ if (!bean_style_) {
+ indent(out) << "return this;" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Unsetter
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public void unset" << cap_name << "() {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "this." << field_name << " = null;" << endl;
+ } else if (issetType == ISSET_PRIMITIVE) {
+ indent(out) << "__isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, "
+ << isset_field_id(field) << ");" << endl;
+ } else {
+ indent(out) << "__isset_bit_vector.clear(" << isset_field_id(field) << ");" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // isSet method
+ indent(out) << "/** Returns true if field " << field_name
+ << " is set (has been assigned a value) and false otherwise */" << endl;
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public boolean is" << get_cap_name("set") << cap_name << "() {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "return this." << field_name << " != null;" << endl;
+ } else if (issetType == ISSET_PRIMITIVE) {
+ indent(out) << "return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, " << isset_field_id(field)
+ << ");" << endl;
+ } else {
+ indent(out) << "return __isset_bit_vector.get(" << isset_field_id(field) << ");" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ if (is_deprecated) {
+ indent(out) << "@Deprecated" << endl;
+ }
+ indent(out) << "public void set" << cap_name << get_cap_name("isSet") << "(boolean value) {"
+ << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "if (!value) {" << endl;
+ indent(out) << " this." << field_name << " = null;" << endl;
+ indent(out) << "}" << endl;
+ } else if (issetType == ISSET_PRIMITIVE) {
+ indent(out) << "__isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, "
+ << isset_field_id(field) << ", value);" << endl;
+ } else {
+ indent(out) << "__isset_bit_vector.set(" << isset_field_id(field) << ", value);" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a toString() method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_struct_tostring(ostream& out, t_struct* tstruct) {
+ out << indent() << "@Override" << endl << indent() << "public java.lang.String toString() {" << endl;
+ indent_up();
+
+ out << indent() << "java.lang.StringBuilder sb = new java.lang.StringBuilder(\"" << tstruct->get_name() << "(\");"
+ << endl;
+ out << indent() << "boolean first = true;" << endl << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ }
+
+ t_field* field = (*f_iter);
+
+ if (!first) {
+ indent(out) << "if (!first) sb.append(\", \");" << endl;
+ }
+ indent(out) << "sb.append(\"" << (*f_iter)->get_name() << ":\");" << endl;
+ bool can_be_null = type_can_be_null(field->get_type());
+ if (can_be_null) {
+ indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out) << " sb.append(\"null\");" << endl;
+ indent(out) << "} else {" << endl;
+ indent_up();
+ }
+
+ if (get_true_type(field->get_type())->is_binary()) {
+ indent(out) << "org.apache.thrift.TBaseHelper.toString(this." << field->get_name() << ", sb);"
+ << endl;
+ } else if ((field->get_type()->is_set())
+ && (get_true_type(((t_set*)field->get_type())->get_elem_type())->is_binary())) {
+ indent(out) << "org.apache.thrift.TBaseHelper.toString(this." << field->get_name() << ", sb);"
+ << endl;
+ } else if ((field->get_type()->is_list())
+ && (get_true_type(((t_list*)field->get_type())->get_elem_type())->is_binary())) {
+ indent(out) << "org.apache.thrift.TBaseHelper.toString(this." << field->get_name() << ", sb);"
+ << endl;
+ } else {
+ indent(out) << "sb.append(this." << (*f_iter)->get_name() << ");" << endl;
+ }
+
+ if (can_be_null) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ indent(out) << "first = false;" << endl;
+
+ if (could_be_unset) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ first = false;
+ }
+ out << indent() << "sb.append(\")\");" << endl << indent() << "return sb.toString();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a static map with meta data to store information such as fieldID to
+ * fieldName mapping
+ *
+ * @param tstruct The struct definition
+ */
+void t_java_generator::generate_java_meta_data_map(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Static Map with fieldID -> org.apache.thrift.meta_data.FieldMetaData mappings
+ indent(out)
+ << "public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;"
+ << endl;
+ indent(out) << "static {" << endl;
+ indent_up();
+
+ indent(out) << "java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new "
+ "java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);"
+ << endl;
+
+ // Populate map
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ std::string field_name = field->get_name();
+ indent(out) << "tmpMap.put(_Fields." << constant_name(field_name)
+ << ", new org.apache.thrift.meta_data.FieldMetaData(\"" << field_name << "\", ";
+
+ // Set field requirement type (required, optional, etc.)
+ if (field->get_req() == t_field::T_REQUIRED) {
+ out << "org.apache.thrift.TFieldRequirementType.REQUIRED, ";
+ } else if (field->get_req() == t_field::T_OPTIONAL) {
+ out << "org.apache.thrift.TFieldRequirementType.OPTIONAL, ";
+ } else {
+ out << "org.apache.thrift.TFieldRequirementType.DEFAULT, ";
+ }
+
+ // Create value meta data
+ generate_field_value_meta_data(out, field->get_type());
+ out << "));" << endl;
+ }
+
+ indent(out) << "metaDataMap = java.util.Collections.unmodifiableMap(tmpMap);" << endl;
+
+ indent(out) << "org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap("
+ << type_name(tstruct) << ".class, metaDataMap);" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Returns a string with the java representation of the given thrift type
+ * (e.g. for the type struct it returns "org.apache.thrift.protocol.TType.STRUCT")
+ */
+std::string t_java_generator::get_java_type_string(t_type* type) {
+ if (type->is_list()) {
+ return "org.apache.thrift.protocol.TType.LIST";
+ } else if (type->is_map()) {
+ return "org.apache.thrift.protocol.TType.MAP";
+ } else if (type->is_set()) {
+ return "org.apache.thrift.protocol.TType.SET";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "org.apache.thrift.protocol.TType.STRUCT";
+ } else if (type->is_enum()) {
+ return "org.apache.thrift.protocol.TType.ENUM";
+ } else if (type->is_typedef()) {
+ return get_java_type_string(((t_typedef*)type)->get_type());
+ } else if (type->is_base_type()) {
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "org.apache.thrift.protocol.TType.VOID";
+ break;
+ case t_base_type::TYPE_STRING:
+ return "org.apache.thrift.protocol.TType.STRING";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "org.apache.thrift.protocol.TType.BOOL";
+ break;
+ case t_base_type::TYPE_I8:
+ return "org.apache.thrift.protocol.TType.BYTE";
+ break;
+ case t_base_type::TYPE_I16:
+ return "org.apache.thrift.protocol.TType.I16";
+ break;
+ case t_base_type::TYPE_I32:
+ return "org.apache.thrift.protocol.TType.I32";
+ break;
+ case t_base_type::TYPE_I64:
+ return "org.apache.thrift.protocol.TType.I64";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ return "org.apache.thrift.protocol.TType.DOUBLE";
+ break;
+ default:
+ throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_java_generator::get_java_type_string!");
+ return "Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_java_generator::get_java_type_string!";
+ break; // This should never happen!
+ }
+ } else {
+ throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_java_generator::get_java_type_string!");
+ // This should never happen!
+ }
+}
+
+void t_java_generator::generate_field_value_meta_data(std::ostream& out, t_type* type) {
+ out << endl;
+ indent_up();
+ indent_up();
+ if (type->is_struct() || type->is_xception()) {
+ indent(out) << "new "
+ "org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType."
+ "STRUCT, " << type_name(type) << ".class";
+ } else if (type->is_container()) {
+ if (type->is_list()) {
+ indent(out)
+ << "new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else if (type->is_set()) {
+ indent(out)
+ << "new org.apache.thrift.meta_data.SetMetaData(org.apache.thrift.protocol.TType.SET, ";
+ t_type* elem_type = ((t_set*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else { // map
+ indent(out)
+ << "new org.apache.thrift.meta_data.MapMetaData(org.apache.thrift.protocol.TType.MAP, ";
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+ generate_field_value_meta_data(out, key_type);
+ out << ", ";
+ generate_field_value_meta_data(out, val_type);
+ }
+ } else if (type->is_enum()) {
+ indent(out)
+ << "new org.apache.thrift.meta_data.EnumMetaData(org.apache.thrift.protocol.TType.ENUM, "
+ << type_name(type) << ".class";
+ } else {
+ indent(out) << "new org.apache.thrift.meta_data.FieldValueMetaData("
+ << get_java_type_string(type);
+ if (type->is_typedef()) {
+ indent(out) << ", \"" << ((t_typedef*)type)->get_symbolic() << "\"";
+ } else if (type->is_binary()) {
+ indent(out) << ", true";
+ }
+ }
+ out << ")";
+ indent_down();
+ indent_down();
+}
+
+/**
+ * Generates a thrift service. In C++, this comprises an entirely separate
+ * header and source file. The header file defines the methods and includes
+ * the data types defined in the main header file, and the implementation
+ * file contains implementations of the basic printer and default interfaces.
+ *
+ * @param tservice The service definition
+ */
+void t_java_generator::generate_service(t_service* tservice) {
+ // Make output file
+ string f_service_name = package_dir_ + "/" + make_valid_java_filename(service_name_) + ".java";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << java_package() << java_suppressions();
+
+ if (!suppress_generated_annotations_) {
+ generate_javax_generated_annotation(f_service_);
+ }
+ f_service_ << "public class " << service_name_ << " {" << endl << endl;
+ indent_up();
+
+ // Generate the three main parts of the service
+ generate_service_interface(tservice);
+ generate_service_async_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_async_client(tservice);
+ generate_service_server(tservice);
+ generate_service_async_server(tservice);
+ generate_service_helpers(tservice);
+
+ indent_down();
+ f_service_ << "}" << endl;
+ f_service_.close();
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_java_generator::generate_service_interface(t_service* tservice) {
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " extends " + extends + ".Iface";
+ }
+
+ generate_java_doc(f_service_, tservice);
+ f_service_ << indent() << "public interface Iface" << extends_iface << " {" << endl << endl;
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_java_doc(f_service_, *f_iter);
+ indent(f_service_) << "public " << function_signature(*f_iter) << ";" << endl << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+void t_java_generator::generate_service_async_interface(t_service* tservice) {
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " extends " + extends + " .AsyncIface";
+ }
+
+ f_service_ << indent() << "public interface AsyncIface" << extends_iface << " {" << endl << endl;
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_service_) << "public " << function_signature_async(*f_iter, true)
+ << " throws org.apache.thrift.TException;" << endl << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates structs for all the service args and return types
+ *
+ * @param tservice The service
+ */
+void t_java_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_java_struct_definition(f_service_, ts, false, true);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_java_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() == NULL) {
+ extends_client = "org.apache.thrift.TServiceClient";
+ } else {
+ extends = type_name(tservice->get_extends());
+ extends_client = extends + ".Client";
+ }
+
+ indent(f_service_) << "public static class Client extends " << extends_client
+ << " implements Iface {" << endl;
+ indent_up();
+
+ indent(f_service_)
+ << "public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {"
+ << endl;
+ indent_up();
+ indent(f_service_) << "public Factory() {}" << endl;
+ indent(f_service_) << "public Client getClient(org.apache.thrift.protocol.TProtocol prot) {"
+ << endl;
+ indent_up();
+ indent(f_service_) << "return new Client(prot);" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ indent(f_service_) << "public Client getClient(org.apache.thrift.protocol.TProtocol iprot, "
+ "org.apache.thrift.protocol.TProtocol oprot) {" << endl;
+ indent_up();
+ indent(f_service_) << "return new Client(iprot, oprot);" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public Client(org.apache.thrift.protocol.TProtocol prot)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "super(prot, prot);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << "public Client(org.apache.thrift.protocol.TProtocol iprot, "
+ "org.apache.thrift.protocol.TProtocol oprot) {" << endl;
+ indent(f_service_) << " super(iprot, oprot);" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+ string sep = "_";
+ string javaname = funname;
+ if (fullcamel_style_) {
+ sep = "";
+ javaname = as_camel_case(funname);
+ }
+
+ // Open function
+ indent(f_service_) << "public " << function_signature(*f_iter) << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "send" << sep << javaname << "(";
+
+ // Get the struct of function call params
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ // Declare the function arguments
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << (*fld_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "recv" << sep << javaname << "();" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ t_function send_function(g_type_void,
+ string("send") + sep + javaname,
+ (*f_iter)->get_arglist());
+
+ string argsname = (*f_iter)->get_name() + "_args";
+
+ // Open function
+ indent(f_service_) << "public " << function_signature(&send_function) << endl;
+ scope_up(f_service_);
+
+ // Serialize the request
+ indent(f_service_) << argsname << " args = new " << argsname << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ indent(f_service_) << "args.set" << get_cap_name((*fld_iter)->get_name()) << "("
+ << (*fld_iter)->get_name() << ");" << endl;
+ }
+
+ const string sendBaseName = (*f_iter)->is_oneway() ? "sendBaseOneway" : "sendBase";
+ indent(f_service_) << sendBaseName << "(\"" << funname << "\", args);" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ string resultname = (*f_iter)->get_name() + "_result";
+
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv") + sep + javaname,
+ &noargs,
+ (*f_iter)->get_xceptions());
+ // Open function
+ indent(f_service_) << "public " << function_signature(&recv_function) << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl
+ << indent() << "receiveBase(result, \"" << funname << "\");" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl
+ << indent() << " return result.success;" << endl << indent() << "}" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl
+ << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl
+ << indent() << "}" << endl;
+ }
+
+ // If you get here it's an exception, unless a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "return;" << endl;
+ } else {
+ f_service_ << indent() << "throw new "
+ "org.apache.thrift.TApplicationException(org.apache.thrift."
+ "TApplicationException.MISSING_RESULT, \""
+ << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
+ }
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl;
+}
+
+void t_java_generator::generate_service_async_client(t_service* tservice) {
+ string extends = "org.apache.thrift.async.TAsyncClient";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends()) + ".AsyncClient";
+ }
+
+ indent(f_service_) << "public static class AsyncClient extends " << extends
+ << " implements AsyncIface {" << endl;
+ indent_up();
+
+ // Factory method
+ indent(f_service_) << "public static class Factory implements "
+ "org.apache.thrift.async.TAsyncClientFactory<AsyncClient> {" << endl;
+ indent(f_service_) << " private org.apache.thrift.async.TAsyncClientManager clientManager;"
+ << endl;
+ indent(f_service_) << " private org.apache.thrift.protocol.TProtocolFactory protocolFactory;"
+ << endl;
+ indent(f_service_) << " public Factory(org.apache.thrift.async.TAsyncClientManager "
+ "clientManager, org.apache.thrift.protocol.TProtocolFactory "
+ "protocolFactory) {" << endl;
+ indent(f_service_) << " this.clientManager = clientManager;" << endl;
+ indent(f_service_) << " this.protocolFactory = protocolFactory;" << endl;
+ indent(f_service_) << " }" << endl;
+ indent(f_service_) << " public AsyncClient "
+ "getAsyncClient(org.apache.thrift.transport.TNonblockingTransport "
+ "transport) {" << endl;
+ indent(f_service_) << " return new AsyncClient(protocolFactory, clientManager, transport);"
+ << endl;
+ indent(f_service_) << " }" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public AsyncClient(org.apache.thrift.protocol.TProtocolFactory "
+ "protocolFactory, org.apache.thrift.async.TAsyncClientManager "
+ "clientManager, org.apache.thrift.transport.TNonblockingTransport "
+ "transport) {" << endl;
+ indent(f_service_) << " super(protocolFactory, clientManager, transport);" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+ string sep = "_";
+ string javaname = funname;
+ if (fullcamel_style_) {
+ sep = "";
+ javaname = as_camel_case(javaname);
+ }
+ t_type* ret_type = (*f_iter)->get_returntype();
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ string funclassname = funname + "_call";
+ const vector<t_field*>& fields = arg_struct->get_members();
+ const std::vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string args_name = (*f_iter)->get_name() + "_args";
+ string result_name = (*f_iter)->get_name() + "_result";
+
+ // Main method body
+ indent(f_service_) << "public " << function_signature_async(*f_iter, false)
+ << " throws org.apache.thrift.TException {" << endl;
+ indent(f_service_) << " checkReady();" << endl;
+ indent(f_service_) << " " << funclassname << " method_call = new " + funclassname + "("
+ << async_argument_list(*f_iter, arg_struct, ret_type)
+ << ", this, ___protocolFactory, ___transport);" << endl;
+ indent(f_service_) << " this.___currentMethod = method_call;" << endl;
+ indent(f_service_) << " ___manager.call(method_call);" << endl;
+ indent(f_service_) << "}" << endl;
+
+ f_service_ << endl;
+
+ // TAsyncMethod object for this function call
+ indent(f_service_) << "public static class " + funclassname
+ + " extends org.apache.thrift.async.TAsyncMethodCall<"
+ + type_name((*f_iter)->get_returntype(), true) + "> {" << endl;
+ indent_up();
+
+ // Member variables
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ indent(f_service_) << "private " + type_name((*fld_iter)->get_type()) + " "
+ + (*fld_iter)->get_name() + ";" << endl;
+ }
+
+ // NOTE since we use a new Client instance to deserialize, let's keep seqid to 0 for now
+ // indent(f_service_) << "private int seqid;" << endl << endl;
+
+ // Constructor
+ indent(f_service_) << "public " + funclassname + "("
+ + async_argument_list(*f_iter, arg_struct, ret_type, true)
+ << ", org.apache.thrift.async.TAsyncClient client, "
+ "org.apache.thrift.protocol.TProtocolFactory protocolFactory, "
+ "org.apache.thrift.transport.TNonblockingTransport transport) throws "
+ "org.apache.thrift.TException {" << endl;
+ indent(f_service_) << " super(client, protocolFactory, transport, resultHandler, "
+ << ((*f_iter)->is_oneway() ? "true" : "false") << ");" << endl;
+
+ // Assign member variables
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ indent(f_service_) << " this." + (*fld_iter)->get_name() + " = " + (*fld_iter)->get_name()
+ + ";" << endl;
+ }
+
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public void write_args(org.apache.thrift.protocol.TProtocol prot) "
+ "throws org.apache.thrift.TException {" << endl;
+ indent_up();
+
+ // Serialize request
+ // NOTE we are leaving seqid as 0, for now (see above)
+ f_service_ << indent() << "prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage(\""
+ << funname << "\", org.apache.thrift.protocol."
+ << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL") << ", 0));"
+ << endl << indent() << args_name << " args = new " << args_name << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args.set" << get_cap_name((*fld_iter)->get_name()) << "("
+ << (*fld_iter)->get_name() << ");" << endl;
+ }
+
+ f_service_ << indent() << "args.write(prot);" << endl << indent() << "prot.writeMessageEnd();"
+ << endl;
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+
+ // Return method
+ indent(f_service_) << "public " + type_name(ret_type, true) + " getResult() throws ";
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << type_name((*x_iter)->get_type(), false, false) + ", ";
+ }
+ f_service_ << "org.apache.thrift.TException {" << endl;
+
+ indent_up();
+ f_service_
+ << indent()
+ << "if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) {"
+ << endl << indent() << " throw new java.lang.IllegalStateException(\"Method call not finished!\");"
+ << endl << indent() << "}" << endl << indent()
+ << "org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new "
+ "org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array());" << endl
+ << indent() << "org.apache.thrift.protocol.TProtocol prot = "
+ "client.getProtocolFactory().getProtocol(memoryTransport);" << endl;
+ indent(f_service_);
+ if (ret_type->is_void()) { // NB: Includes oneways which always return void.
+ f_service_ << "return null;" << endl;
+ } else {
+ f_service_ << "return (new Client(prot)).recv" + sep + javaname + "();" << endl;
+ }
+
+ // Close function
+ indent_down();
+ indent(f_service_) << "}" << endl;
+
+ // Close class
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+ }
+
+ // Close AsyncClient
+ scope_down(f_service_);
+ f_service_ << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_java_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Extends stuff
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() == NULL) {
+ extends_processor = "org.apache.thrift.TBaseProcessor<I>";
+ } else {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".Processor<I>";
+ }
+
+ // Generate the header portion
+ indent(f_service_) << "public static class Processor<I extends Iface> extends "
+ << extends_processor << " implements org.apache.thrift.TProcessor {" << endl;
+ indent_up();
+
+ indent(f_service_)
+ << "private static final org.slf4j.Logger _LOGGER = org.slf4j.LoggerFactory.getLogger(Processor.class.getName());"
+ << endl;
+
+ indent(f_service_) << "public Processor(I iface) {" << endl;
+ indent(f_service_) << " super(iface, getProcessMap(new java.util.HashMap<java.lang.String, "
+ "org.apache.thrift.ProcessFunction<I, ? extends "
+ "org.apache.thrift.TBase>>()));" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "protected Processor(I iface, java.util.Map<java.lang.String, "
+ "org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>> "
+ "processMap) {" << endl;
+ indent(f_service_) << " super(iface, getProcessMap(processMap));" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "private static <I extends Iface> java.util.Map<java.lang.String, "
+ "org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>> "
+ "getProcessMap(java.util.Map<java.lang.String, org.apache.thrift.ProcessFunction<I, ? extends "
+ " org.apache.thrift.TBase>> processMap) {" << endl;
+ indent_up();
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_service_) << "processMap.put(\"" << (*f_iter)->get_name() << "\", new "
+ << (*f_iter)->get_name() << "());" << endl;
+ }
+ indent(f_service_) << "return processMap;" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_java_generator::generate_service_async_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Extends stuff
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() == NULL) {
+ extends_processor = "org.apache.thrift.TBaseAsyncProcessor<I>";
+ } else {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".AsyncProcessor<I>";
+ }
+
+ // Generate the header portion
+ indent(f_service_) << "public static class AsyncProcessor<I extends AsyncIface> extends "
+ << extends_processor << " {" << endl;
+ indent_up();
+
+ indent(f_service_) << "private static final org.slf4j.Logger _LOGGER = "
+ "org.slf4j.LoggerFactory.getLogger(AsyncProcessor.class.getName());" << endl;
+
+ indent(f_service_) << "public AsyncProcessor(I iface) {" << endl;
+ indent(f_service_) << " super(iface, getProcessMap(new java.util.HashMap<java.lang.String, "
+ "org.apache.thrift.AsyncProcessFunction<I, ? extends "
+ "org.apache.thrift.TBase, ?>>()));" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "protected AsyncProcessor(I iface, java.util.Map<java.lang.String, "
+ "org.apache.thrift.AsyncProcessFunction<I, ? extends "
+ "org.apache.thrift.TBase, ?>> processMap) {" << endl;
+ indent(f_service_) << " super(iface, getProcessMap(processMap));" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "private static <I extends AsyncIface> java.util.Map<java.lang.String, "
+ "org.apache.thrift.AsyncProcessFunction<I, ? extends "
+ "org.apache.thrift.TBase,?>> getProcessMap(java.util.Map<java.lang.String, "
+ "org.apache.thrift.AsyncProcessFunction<I, ? extends "
+ "org.apache.thrift.TBase, ?>> processMap) {" << endl;
+ indent_up();
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_service_) << "processMap.put(\"" << (*f_iter)->get_name() << "\", new "
+ << (*f_iter)->get_name() << "());" << endl;
+ }
+ indent(f_service_) << "return processMap;" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_async_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_java_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_java_struct_definition(f_service_, &result, false, true, true);
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_java_generator::generate_process_async_function(t_service* tservice, t_function* tfunction) {
+ string argsname = tfunction->get_name() + "_args";
+
+ string resultname = tfunction->get_name() + "_result";
+ if (tfunction->is_oneway()) {
+ resultname = "org.apache.thrift.TBase";
+ }
+
+ string resulttype = type_name(tfunction->get_returntype(), true);
+
+ (void)tservice;
+ // Open class
+ indent(f_service_) << "public static class " << tfunction->get_name()
+ << "<I extends AsyncIface> extends org.apache.thrift.AsyncProcessFunction<I, "
+ << argsname << ", " << resulttype << "> {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public " << tfunction->get_name() << "() {" << endl;
+ indent(f_service_) << " super(\"" << tfunction->get_name() << "\");" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public " << argsname << " getEmptyArgsInstance() {" << endl;
+ indent(f_service_) << " return new " << argsname << "();" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public org.apache.thrift.async.AsyncMethodCallback<" << resulttype
+ << "> getResultHandler(final org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer fb, final int seqid) {" << endl;
+ indent_up();
+ indent(f_service_) << "final org.apache.thrift.AsyncProcessFunction fcall = this;" << endl;
+ indent(f_service_) << "return new org.apache.thrift.async.AsyncMethodCallback<" << resulttype
+ << ">() { " << endl;
+ indent_up();
+ indent(f_service_) << "public void onComplete(" << resulttype << " o) {" << endl;
+
+ indent_up();
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << resultname << " result = new " << resultname << "();" << endl;
+
+ if (!tfunction->get_returntype()->is_void()) {
+ indent(f_service_) << "result.success = o;" << endl;
+ // Set isset on success field
+ if (!type_can_be_null(tfunction->get_returntype())) {
+ indent(f_service_) << "result.set" << get_cap_name("success") << get_cap_name("isSet")
+ << "(true);" << endl;
+ }
+ }
+
+ indent(f_service_) << "try {" << endl;
+ indent(f_service_)
+ << " fcall.sendResponse(fb, result, org.apache.thrift.protocol.TMessageType.REPLY,seqid);"
+ << endl;
+ indent(f_service_) << "} catch (org.apache.thrift.transport.TTransportException e) {" << endl;
+ indent_up();
+ f_service_ << indent()
+ << "_LOGGER.error(\"TTransportException writing to internal frame buffer\", e);"
+ << endl
+ << indent() << "fb.close();" << endl;
+ indent_down();
+ indent(f_service_) << "} catch (java.lang.Exception e) {" << endl;
+ indent_up();
+ f_service_ << indent() << "_LOGGER.error(\"Exception writing to internal frame buffer\", e);"
+ << endl
+ << indent() << "onError(e);" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}" << endl;
+
+ indent(f_service_) << "public void onError(java.lang.Exception e) {" << endl;
+ indent_up();
+
+ if (tfunction->is_oneway()) {
+ indent(f_service_) << "if (e instanceof org.apache.thrift.transport.TTransportException) {"
+ << endl;
+ indent_up();
+
+ f_service_ << indent() << "_LOGGER.error(\"TTransportException inside handler\", e);" << endl
+ << indent() << "fb.close();" << endl;
+
+ indent_down();
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+
+ f_service_ << indent() << "_LOGGER.error(\"Exception inside oneway handler\", e);" << endl;
+
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "byte msgType = org.apache.thrift.protocol.TMessageType.REPLY;" << endl;
+ indent(f_service_) << "org.apache.thrift.TSerializable msg;" << endl;
+ indent(f_service_) << resultname << " result = new " << resultname << "();" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+
+ vector<t_field*>::const_iterator x_iter;
+ if (xceptions.size() > 0) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ if (x_iter == xceptions.begin())
+ f_service_ << indent();
+ string type = type_name((*x_iter)->get_type(), false, false);
+ string name = (*x_iter)->get_name();
+ f_service_ << "if (e instanceof " << type << ") {" << endl;
+ indent_up();
+ f_service_ << indent() << "result." << name << " = (" << type << ") e;" << endl
+ << indent() << "result.set" << get_cap_name(name) << get_cap_name("isSet")
+ << "(true);" << endl
+ << indent() << "msg = result;" << endl;
+ indent_down();
+ indent(f_service_) << "} else ";
+ }
+ } else {
+ indent(f_service_);
+ }
+ f_service_ << "if (e instanceof org.apache.thrift.transport.TTransportException) {" << endl;
+ indent_up();
+ f_service_ << indent() << "_LOGGER.error(\"TTransportException inside handler\", e);" << endl
+ << indent() << "fb.close();" << endl
+ << indent() << "return;" << endl;
+ indent_down();
+ indent(f_service_) << "} else if (e instanceof org.apache.thrift.TApplicationException) {"
+ << endl;
+ indent_up();
+ f_service_ << indent() << "_LOGGER.error(\"TApplicationException inside handler\", e);" << endl
+ << indent() << "msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION;" << endl
+ << indent() << "msg = (org.apache.thrift.TApplicationException)e;" << endl;
+ indent_down();
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+ f_service_ << indent() << "_LOGGER.error(\"Exception inside handler\", e);" << endl
+ << indent() << "msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION;" << endl
+ << indent() << "msg = new "
+ "org.apache.thrift.TApplicationException(org.apache.thrift."
+ "TApplicationException.INTERNAL_ERROR, e.getMessage());"
+ << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl
+ << indent() << "try {" << endl
+ << indent() << " fcall.sendResponse(fb,msg,msgType,seqid);" << endl
+ << indent() << "} catch (java.lang.Exception ex) {" << endl
+ << indent() << " _LOGGER.error(\"Exception writing to internal frame buffer\", ex);"
+ << endl
+ << indent() << " fb.close();" << endl
+ << indent() << "}" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ indent_down();
+ indent(f_service_) << "};" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "protected boolean isOneway() {" << endl;
+ indent(f_service_) << " return " << ((tfunction->is_oneway()) ? "true" : "false") << ";" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public void start(I iface, " << argsname
+ << " args, org.apache.thrift.async.AsyncMethodCallback<" << resulttype
+ << "> resultHandler) throws org.apache.thrift.TException {" << endl;
+ indent_up();
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ f_service_ << indent();
+
+ f_service_ << "iface." << get_rpc_method_name(tfunction->get_name()) << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ if (!first)
+ f_service_ << ",";
+ f_service_ << "resultHandler";
+ f_service_ << ");" << endl;
+
+ indent_down();
+ indent(f_service_) << "}";
+
+ // Close function
+ f_service_ << endl;
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_java_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+ if (tfunction->is_oneway()) {
+ resultname = "org.apache.thrift.TBase";
+ }
+
+ (void)tservice;
+ // Open class
+ indent(f_service_) << "public static class " << tfunction->get_name()
+ << "<I extends Iface> extends org.apache.thrift.ProcessFunction<I, "
+ << argsname << "> {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public " << tfunction->get_name() << "() {" << endl;
+ indent(f_service_) << " super(\"" << tfunction->get_name() << "\");" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public " << argsname << " getEmptyArgsInstance() {" << endl;
+ indent(f_service_) << " return new " << argsname << "();" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "protected boolean isOneway() {" << endl;
+ indent(f_service_) << " return " << ((tfunction->is_oneway()) ? "true" : "false") << ";" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "@Override" << endl;
+ indent(f_service_) << "protected boolean rethrowUnhandledExceptions() {" << endl;
+ indent(f_service_) << " return " << ((rethrow_unhandled_exceptions_) ? "true" : "false") << ";" << endl;
+ indent(f_service_) << "}" << endl << endl;
+
+ indent(f_service_) << "public " << resultname << " getResult(I iface, " << argsname
+ << " args) throws org.apache.thrift.TException {" << endl;
+ indent_up();
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << resultname << " result = new " << resultname << "();" << endl;
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "try {" << endl;
+ indent_up();
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ f_service_ << indent();
+
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.success = ";
+ }
+ f_service_ << "iface." << get_rpc_method_name(tfunction->get_name()) << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ // Set isset on success field
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()
+ && !type_can_be_null(tfunction->get_returntype())) {
+ indent(f_service_) << "result.set" << get_cap_name("success") << get_cap_name("isSet")
+ << "(true);" << endl;
+ }
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ indent_down();
+ f_service_ << indent() << "}";
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << " catch (" << type_name((*x_iter)->get_type(), false, false) << " "
+ << (*x_iter)->get_name() << ") {" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ f_service_ << indent() << "result." << (*x_iter)->get_name() << " = "
+ << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ f_service_ << indent() << "}";
+ } else {
+ f_service_ << "}";
+ }
+ }
+ f_service_ << endl;
+ }
+
+ if (tfunction->is_oneway()) {
+ indent(f_service_) << "return null;" << endl;
+ } else {
+ indent(f_service_) << "return result;" << endl;
+ }
+ indent_down();
+ indent(f_service_) << "}";
+
+ // Close function
+ f_service_ << endl;
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ *
+ * @param tfield The field
+ * @param prefix The variable name or container for this field
+ */
+void t_java_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool has_metadata) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name, has_metadata);
+ } else if (type->is_base_type()) {
+ indent(out) << name << " = iprot.";
+
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "readBinary();";
+ } else {
+ out << "readString();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble();";
+ break;
+ default:
+ throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
+ }
+ out << endl;
+ } else if (type->is_enum()) {
+ indent(out) << name << " = "
+ << type_name(tfield->get_type(), true, false, false, true)
+ + ".findByValue(iprot.readI32());" << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, invokes read()
+ */
+void t_java_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+
+ if (reuse_objects_) {
+ indent(out) << "if (" << prefix << " == null) {" << endl;
+ indent_up();
+ }
+ indent(out) << prefix << " = new " << type_name(tstruct) << "();" << endl;
+ if (reuse_objects_) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ indent(out) << prefix << ".read(iprot);" << endl;
+}
+
+/**
+ * Deserializes a container by reading its size and then iterating
+ */
+void t_java_generator::generate_deserialize_container(ostream& out,
+ t_type* ttype,
+ string prefix,
+ bool has_metadata) {
+
+ scope_up(out);
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ if (has_metadata) {
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "org.apache.thrift.protocol.TMap " << obj << " = iprot.readMapBegin();"
+ << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "org.apache.thrift.protocol.TSet " << obj << " = iprot.readSetBegin();"
+ << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "org.apache.thrift.protocol.TList " << obj << " = iprot.readListBegin();"
+ << endl;
+ }
+ } else {
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "org.apache.thrift.protocol.TMap " << obj
+ << " = new org.apache.thrift.protocol.TMap("
+ << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "iprot.readI32());" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "org.apache.thrift.protocol.TSet " << obj
+ << " = new org.apache.thrift.protocol.TSet("
+ << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", iprot.readI32());"
+ << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "org.apache.thrift.protocol.TList " << obj
+ << " = new org.apache.thrift.protocol.TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", iprot.readI32());"
+ << endl;
+ }
+ }
+
+ if (reuse_objects_) {
+ indent(out) << "if (" << prefix << " == null) {" << endl;
+ indent_up();
+ }
+
+ if (is_enum_set(ttype)) {
+ out << indent() << prefix << " = " << type_name(ttype, false, true, true) << ".noneOf";
+ } else {
+ out << indent() << prefix << " = new " << type_name(ttype, false, true);
+ }
+
+ // construct the collection correctly i.e. with appropriate size/type
+ if (is_enum_set(ttype) || is_enum_map(ttype)) {
+ out << "(" << inner_enum_type_name(ttype) << ");" << endl;
+ } else if (sorted_containers_ && (ttype->is_map() || ttype->is_set())) {
+ // TreeSet and TreeMap don't have any constructor which takes a capacity as an argument
+ out << "();" << endl;
+ } else {
+ out << "(" << (ttype->is_list() ? "" : "2*") << obj << ".size"
+ << ");" << endl;
+ }
+
+ if (reuse_objects_) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix, obj, has_metadata);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix, obj, has_metadata);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix, obj, has_metadata);
+ }
+
+ scope_down(out);
+
+ if (has_metadata) {
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot.readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot.readListEnd();" << endl;
+ }
+ }
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_java_generator::generate_deserialize_map_element(ostream& out,
+ t_map* tmap,
+ string prefix,
+ string obj,
+ bool has_metadata) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey, reuse_objects_, false) << endl;
+ indent(out) << declare_field(&fval, reuse_objects_, false) << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size"
+ << "; "
+ << "++" << i << ")" << endl;
+
+ scope_up(out);
+
+ generate_deserialize_field(out, &fkey, "", has_metadata);
+ generate_deserialize_field(out, &fval, "", has_metadata);
+
+ if (get_true_type(fkey.get_type())->is_enum()) {
+ indent(out) << "if (" << key << " != null)" << endl;
+ scope_up(out);
+ }
+
+ indent(out) << prefix << ".put(" << key << ", " << val << ");" << endl;
+
+ if (get_true_type(fkey.get_type())->is_enum()) {
+ scope_down(out);
+ }
+
+ if (reuse_objects_ && !get_true_type(fkey.get_type())->is_base_type()) {
+ indent(out) << key << " = null;" << endl;
+ }
+
+ if (reuse_objects_ && !get_true_type(fval.get_type())->is_base_type()) {
+ indent(out) << val << " = null;" << endl;
+ }
+}
+
+/**
+ * Deserializes a set element
+ */
+void t_java_generator::generate_deserialize_set_element(ostream& out,
+ t_set* tset,
+ string prefix,
+ string obj,
+ bool has_metadata) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem, reuse_objects_, false) << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size"
+ << "; "
+ << "++" << i << ")" << endl;
+ scope_up(out);
+
+ generate_deserialize_field(out, &felem, "", has_metadata);
+
+ if (get_true_type(felem.get_type())->is_enum()) {
+ indent(out) << "if (" << elem << " != null)" << endl;
+ scope_up(out);
+ }
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+
+ if (get_true_type(felem.get_type())->is_enum()) {
+ scope_down(out);
+ }
+
+ if (reuse_objects_ && !get_true_type(felem.get_type())->is_base_type()) {
+ indent(out) << elem << " = null;" << endl;
+ }
+}
+
+/**
+ * Deserializes a list element
+ */
+void t_java_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix,
+ string obj,
+ bool has_metadata) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem, reuse_objects_, false) << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size"
+ << "; "
+ << "++" << i << ")" << endl;
+ scope_up(out);
+
+ generate_deserialize_field(out, &felem, "", has_metadata);
+
+ if (get_true_type(felem.get_type())->is_enum()) {
+ indent(out) << "if (" << elem << " != null)" << endl;
+ scope_up(out);
+ }
+
+ indent(out) << prefix << ".add(" << elem << ");" << endl;
+
+ if (get_true_type(felem.get_type())->is_enum()) {
+ scope_down(out);
+ }
+
+ if (reuse_objects_ && !get_true_type(felem.get_type())->is_base_type()) {
+ indent(out) << elem << " = null;" << endl;
+ }
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_java_generator::generate_serialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool has_metadata) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name(), has_metadata);
+ } else if (type->is_enum()) {
+ indent(out) << "oprot.writeI32(" << prefix + tfield->get_name() << ".getValue());" << endl;
+ } else if (type->is_base_type()) {
+ string name = prefix + tfield->get_name();
+ indent(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ");";
+ } else {
+ out << "writeString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(struct." << name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_java_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ out << indent() << prefix << ".write(oprot);" << endl;
+}
+
+/**
+ * Serializes a container by writing its size then the elements.
+ *
+ * @param ttype The type of container
+ * @param prefix String prefix for fields
+ */
+void t_java_generator::generate_serialize_container(ostream& out,
+ t_type* ttype,
+ string prefix,
+ bool has_metadata) {
+ scope_up(out);
+
+ if (has_metadata) {
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapBegin(new org.apache.thrift.protocol.TMap("
+ << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".size()));"
+ << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetBegin(new org.apache.thrift.protocol.TSet("
+ << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix
+ << ".size()));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListBegin(new org.apache.thrift.protocol.TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix
+ << ".size()));" << endl;
+ }
+ } else {
+ indent(out) << "oprot.writeI32(" << prefix << ".size());" << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map()) {
+ indent(out) << "for (java.util.Map.Entry<" << type_name(((t_map*)ttype)->get_key_type(), true, false)
+ << ", " << type_name(((t_map*)ttype)->get_val_type(), true, false) << "> " << iter
+ << " : " << prefix << ".entrySet())";
+ } else if (ttype->is_set()) {
+ indent(out) << "for (" << type_name(((t_set*)ttype)->get_elem_type()) << " " << iter << " : "
+ << prefix << ")";
+ } else if (ttype->is_list()) {
+ indent(out) << "for (" << type_name(((t_list*)ttype)->get_elem_type()) << " " << iter << " : "
+ << prefix << ")";
+ }
+
+ out << endl;
+ scope_up(out);
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter, prefix, has_metadata);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter, has_metadata);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter, has_metadata);
+ }
+ scope_down(out);
+
+ if (has_metadata) {
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListEnd();" << endl;
+ }
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ */
+void t_java_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string iter,
+ string map,
+ bool has_metadata) {
+ (void)map;
+ t_field kfield(tmap->get_key_type(), iter + ".getKey()");
+ generate_serialize_field(out, &kfield, "", has_metadata);
+ t_field vfield(tmap->get_val_type(), iter + ".getValue()");
+ generate_serialize_field(out, &vfield, "", has_metadata);
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_java_generator::generate_serialize_set_element(ostream& out,
+ t_set* tset,
+ string iter,
+ bool has_metadata) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "", has_metadata);
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_java_generator::generate_serialize_list_element(ostream& out,
+ t_list* tlist,
+ string iter,
+ bool has_metadata) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "", has_metadata);
+}
+
+/**
+ * Returns a Java type name
+ *
+ * @param ttype The type
+ * @param container Is the type going inside a container?
+ * @return Java type name, i.e. java.util.HashMap<Key,Value>
+ */
+string t_java_generator::type_name(t_type* ttype,
+ bool in_container,
+ bool in_init,
+ bool skip_generic,
+ bool force_namespace) {
+ // In Java typedefs are just resolved to their real type
+ ttype = get_true_type(ttype);
+ string prefix;
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype, in_container);
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ if (in_init) {
+ if (is_enum_map(tmap)) {
+ prefix = "java.util.EnumMap";
+ } else if (sorted_containers_) {
+ prefix = "java.util.TreeMap";
+ } else {
+ prefix = "java.util.HashMap";
+ }
+ } else {
+ prefix = "java.util.Map";
+ }
+ return prefix + (skip_generic ? "" : "<" + type_name(tmap->get_key_type(), true) + ","
+ + type_name(tmap->get_val_type(), true) + ">");
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ if (in_init) {
+ if (is_enum_set(tset)) {
+ prefix = "java.util.EnumSet";
+ } else if (sorted_containers_) {
+ prefix = "java.util.TreeSet";
+ } else {
+ prefix = "java.util.HashSet";
+ }
+ } else {
+ prefix = "java.util.Set";
+ }
+ return prefix + (skip_generic ? "" : "<" + type_name(tset->get_elem_type(), true) + ">");
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ if (in_init) {
+ prefix = "java.util.ArrayList";
+ } else {
+ prefix = "java.util.List";
+ }
+ return prefix + (skip_generic ? "" : "<" + type_name(tlist->get_elem_type(), true) + ">");
+ }
+
+ // Check for namespacing
+ t_program* program = ttype->get_program();
+ if ((program != NULL) && ((program != program_) || force_namespace)) {
+ string package = program->get_namespace("java");
+ if (!package.empty()) {
+ return package + "." + ttype->get_name();
+ }
+ }
+
+ return ttype->get_name();
+}
+
+/**
+ * Returns the Java type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ * @param container Is it going in a Java container?
+ */
+string t_java_generator::base_type_name(t_base_type* type, bool in_container) {
+ t_base_type::t_base tbase = type->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return (in_container ? "Void" : "void");
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ return "java.nio.ByteBuffer";
+ } else {
+ return "java.lang.String";
+ }
+ case t_base_type::TYPE_BOOL:
+ return (in_container ? "java.lang.Boolean" : "boolean");
+ case t_base_type::TYPE_I8:
+ return (in_container ? "java.lang.Byte" : "byte");
+ case t_base_type::TYPE_I16:
+ return (in_container ? "java.lang.Short" : "short");
+ case t_base_type::TYPE_I32:
+ return (in_container ? "java.lang.Integer" : "int");
+ case t_base_type::TYPE_I64:
+ return (in_container ? "java.lang.Long" : "long");
+ case t_base_type::TYPE_DOUBLE:
+ return (in_container ? "java.lang.Double" : "double");
+ default:
+ throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param tfield The field
+ * @param init Whether to initialize the field
+ */
+string t_java_generator::declare_field(t_field* tfield, bool init, bool comment) {
+ // TODO(mcslee): do we ever need to initialize the field?
+ string result = "";
+ t_type* ttype = get_true_type(tfield->get_type());
+ if (type_can_be_null(ttype)) {
+ result += java_nullable_annotation() + " ";
+ }
+ result += type_name(tfield->get_type()) + " " + tfield->get_name();
+ if (init) {
+ if (ttype->is_base_type() && tfield->get_value() != NULL) {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, ttype, tfield->get_value());
+ } else if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+ } else if (ttype->is_enum()) {
+ result += " = null";
+ } else if (ttype->is_container()) {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ } else {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ ;
+ }
+ }
+ result += ";";
+ if (comment) {
+ result += " // ";
+ if (tfield->get_req() == t_field::T_OPTIONAL) {
+ result += "optional";
+ } else {
+ result += "required";
+ }
+ }
+ return result;
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_java_generator::function_signature(t_function* tfunction, string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ std::string fn_name = get_rpc_method_name(tfunction->get_name());
+ std::string result = type_name(ttype) + " " + prefix + fn_name + "("
+ + argument_list(tfunction->get_arglist()) + ") throws ";
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ result += type_name((*x_iter)->get_type(), false, false) + ", ";
+ }
+ result += "org.apache.thrift.TException";
+ return result;
+}
+
+/**
+ * Renders a function signature of the form 'void name(args, resultHandler)'
+ *
+ * @params tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_java_generator::function_signature_async(t_function* tfunction,
+ bool use_base_method,
+ string prefix) {
+ std::string arglist = async_function_call_arglist(tfunction, use_base_method, true);
+
+ std::string ret_type = "";
+ if (use_base_method) {
+ ret_type += "AsyncClient.";
+ }
+ ret_type += tfunction->get_name() + "_call";
+
+ std::string fn_name = get_rpc_method_name(tfunction->get_name());
+
+ std::string result = prefix + "void " + fn_name + "(" + arglist + ")";
+ return result;
+}
+
+string t_java_generator::async_function_call_arglist(t_function* tfunc,
+ bool use_base_method,
+ bool include_types) {
+ (void)use_base_method;
+ std::string arglist = "";
+ if (tfunc->get_arglist()->get_members().size() > 0) {
+ arglist = argument_list(tfunc->get_arglist(), include_types) + ", ";
+ }
+
+ if (include_types) {
+ arglist += "org.apache.thrift.async.AsyncMethodCallback<";
+ arglist += type_name(tfunc->get_returntype(), true) + "> ";
+ }
+ arglist += "resultHandler";
+
+ return arglist;
+}
+
+/**
+ * Renders a comma separated field list, with type names
+ */
+string t_java_generator::argument_list(t_struct* tstruct, bool include_types) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ if (include_types) {
+ result += type_name((*f_iter)->get_type()) + " ";
+ }
+ result += (*f_iter)->get_name();
+ }
+ return result;
+}
+
+string t_java_generator::async_argument_list(t_function* tfunct,
+ t_struct* tstruct,
+ t_type* ttype,
+ bool include_types) {
+ (void)tfunct;
+ (void)ttype;
+ string result = "";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ if (include_types) {
+ result += type_name((*f_iter)->get_type()) + " ";
+ }
+ result += (*f_iter)->get_name();
+ }
+ if (!first) {
+ result += ", ";
+ }
+ if (include_types) {
+ result += "org.apache.thrift.async.AsyncMethodCallback<";
+ result += type_name(tfunct->get_returntype(), true) + "> ";
+ }
+ result += "resultHandler";
+ return result;
+}
+
+/**
+ * Converts the parse type to a Java enum string for the given type.
+ */
+string t_java_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "org.apache.thrift.protocol.TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "org.apache.thrift.protocol.TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "org.apache.thrift.protocol.TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "org.apache.thrift.protocol.TType.I16";
+ case t_base_type::TYPE_I32:
+ return "org.apache.thrift.protocol.TType.I32";
+ case t_base_type::TYPE_I64:
+ return "org.apache.thrift.protocol.TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "org.apache.thrift.protocol.TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "org.apache.thrift.protocol.TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "org.apache.thrift.protocol.TType.STRUCT";
+ } else if (type->is_map()) {
+ return "org.apache.thrift.protocol.TType.MAP";
+ } else if (type->is_set()) {
+ return "org.apache.thrift.protocol.TType.SET";
+ } else if (type->is_list()) {
+ return "org.apache.thrift.protocol.TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Takes a name and produes a valid Java source file name from it
+ *
+ * @param fromName The name which shall become a valid Java source file name
+ * @return The produced identifier
+ */
+std::string t_java_generator::make_valid_java_filename(std::string const& fromName) {
+ // if any further rules apply to source file names in Java, modify as necessary
+ return make_valid_java_identifier(fromName);
+}
+
+/**
+ * Takes a name and produes a valid Java identifier from it
+ *
+ * @param fromName The name which shall become a valid Java identifier
+ * @return The produced identifier
+ */
+std::string t_java_generator::make_valid_java_identifier(std::string const& fromName) {
+ std::string str = fromName;
+ if (str.empty()) {
+ return str;
+ }
+
+ // tests rely on this
+ assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+ // if the first letter is a number, we add an additional underscore in front of it
+ char c = str.at(0);
+ if (('0' <= c) && (c <= '9')) {
+ str = "_" + str;
+ }
+
+ // following chars: letter, number or underscore
+ for (size_t i = 0; i < str.size(); ++i) {
+ c = str.at(i);
+ if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
+ && ('_' != c)) {
+ str.replace(i, 1, "_");
+ }
+ }
+
+ return str;
+}
+
+std::string t_java_generator::as_camel_case(std::string name, bool ucfirst) {
+ std::string new_name;
+ size_t i = 0;
+ for (i = 0; i < name.size(); i++) {
+ if (name[i] != '_')
+ break;
+ }
+ if (ucfirst) {
+ new_name += toupper(name[i++]);
+ } else {
+ new_name += tolower(name[i++]);
+ }
+ for (; i < name.size(); i++) {
+ if (name[i] == '_') {
+ if (i < name.size() - 1) {
+ i++;
+ new_name += toupper(name[i]);
+ }
+ } else {
+ new_name += name[i];
+ }
+ }
+ return new_name;
+}
+
+std::string t_java_generator::get_rpc_method_name(std::string name) {
+ if (fullcamel_style_) {
+ return as_camel_case(name, false);
+ } else {
+ return name;
+ }
+}
+
+/**
+ * Applies the correct style to a string based on the value of nocamel_style_
+ * and/or fullcamel_style_
+ */
+std::string t_java_generator::get_cap_name(std::string name) {
+ if (nocamel_style_) {
+ return "_" + name;
+ } else if (fullcamel_style_) {
+ return as_camel_case(name);
+ } else {
+ name[0] = toupper(name[0]);
+ return name;
+ }
+}
+
+string t_java_generator::constant_name(string name) {
+ string constant_name;
+
+ bool is_first = true;
+ bool was_previous_char_upper = false;
+ for (char character : name) {
+ bool is_upper = isupper(character);
+
+ if (is_upper && !is_first && !was_previous_char_upper) {
+ constant_name += '_';
+ }
+ constant_name += toupper(character);
+
+ is_first = false;
+ was_previous_char_upper = is_upper;
+ }
+
+ return constant_name;
+}
+
+void t_java_generator::generate_deep_copy_container(ostream& out,
+ std::string source_name_p1,
+ std::string source_name_p2,
+ std::string result_name,
+ t_type* type) {
+
+ t_container* container = (t_container*)type;
+ std::string source_name;
+ if (source_name_p2 == "")
+ source_name = source_name_p1;
+ else
+ source_name = source_name_p1 + "." + source_name_p2;
+
+ bool copy_construct_container;
+ if (container->is_map()) {
+ t_map* tmap = (t_map*)container;
+ copy_construct_container = tmap->get_key_type()->is_base_type()
+ && tmap->get_val_type()->is_base_type();
+ } else {
+ t_type* elem_type = container->is_list() ? ((t_list*)container)->get_elem_type()
+ : ((t_set*)container)->get_elem_type();
+ copy_construct_container = elem_type->is_base_type();
+ }
+
+ if (copy_construct_container) {
+ // deep copy of base types can be done much more efficiently than iterating over all the
+ // elements manually
+ indent(out) << type_name(type, true, false) << " " << result_name << " = new "
+ << type_name(container, false, true) << "(" << source_name << ");" << endl;
+ return;
+ }
+
+ std::string constructor_args;
+ if (is_enum_set(container) || is_enum_map(container)) {
+ constructor_args = inner_enum_type_name(container);
+ } else if (!(sorted_containers_ && (container->is_map() || container->is_set()))) {
+ // unsorted containers accept a capacity value
+ constructor_args = source_name + ".size()";
+ }
+
+ if (is_enum_set(container)) {
+ indent(out) << type_name(type, true, false) << " " << result_name << " = "
+ << type_name(container, false, true, true) << ".noneOf(" << constructor_args << ");" << endl;
+ } else {
+ indent(out) << type_name(type, true, false) << " " << result_name << " = new "
+ << type_name(container, false, true) << "(" << constructor_args << ");" << endl;
+ }
+
+ std::string iterator_element_name = source_name_p1 + "_element";
+ std::string result_element_name = result_name + "_copy";
+
+ if (container->is_map()) {
+ t_type* key_type = ((t_map*)container)->get_key_type();
+ t_type* val_type = ((t_map*)container)->get_val_type();
+
+ indent(out) << "for (java.util.Map.Entry<" << type_name(key_type, true, false) << ", "
+ << type_name(val_type, true, false) << "> " << iterator_element_name << " : "
+ << source_name << ".entrySet()) {" << endl;
+ indent_up();
+
+ out << endl;
+
+ indent(out) << type_name(key_type, true, false) << " " << iterator_element_name
+ << "_key = " << iterator_element_name << ".getKey();" << endl;
+ indent(out) << type_name(val_type, true, false) << " " << iterator_element_name
+ << "_value = " << iterator_element_name << ".getValue();" << endl;
+
+ out << endl;
+
+ if (key_type->is_container()) {
+ generate_deep_copy_container(out,
+ iterator_element_name + "_key",
+ "",
+ result_element_name + "_key",
+ key_type);
+ } else {
+ indent(out) << type_name(key_type, true, false) << " " << result_element_name << "_key = ";
+ generate_deep_copy_non_container(out,
+ iterator_element_name + "_key",
+ result_element_name + "_key",
+ key_type);
+ out << ";" << endl;
+ }
+
+ out << endl;
+
+ if (val_type->is_container()) {
+ generate_deep_copy_container(out,
+ iterator_element_name + "_value",
+ "",
+ result_element_name + "_value",
+ val_type);
+ } else {
+ indent(out) << type_name(val_type, true, false) << " " << result_element_name << "_value = ";
+ generate_deep_copy_non_container(out,
+ iterator_element_name + "_value",
+ result_element_name + "_value",
+ val_type);
+ out << ";" << endl;
+ }
+
+ out << endl;
+
+ indent(out) << result_name << ".put(" << result_element_name << "_key, " << result_element_name
+ << "_value);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ } else {
+ t_type* elem_type;
+
+ if (container->is_set()) {
+ elem_type = ((t_set*)container)->get_elem_type();
+ } else {
+ elem_type = ((t_list*)container)->get_elem_type();
+ }
+
+ indent(out) << "for (" << type_name(elem_type, true, false) << " " << iterator_element_name
+ << " : " << source_name << ") {" << endl;
+
+ indent_up();
+
+ if (elem_type->is_container()) {
+ // recursive deep copy
+ generate_deep_copy_container(out, iterator_element_name, "", result_element_name, elem_type);
+ indent(out) << result_name << ".add(" << result_element_name << ");" << endl;
+ } else {
+ // iterative copy
+ if (elem_type->is_binary()) {
+ indent(out) << "java.nio.ByteBuffer temp_binary_element = ";
+ generate_deep_copy_non_container(out,
+ iterator_element_name,
+ "temp_binary_element",
+ elem_type);
+ out << ";" << endl;
+ indent(out) << result_name << ".add(temp_binary_element);" << endl;
+ } else {
+ indent(out) << result_name << ".add(";
+ generate_deep_copy_non_container(out, iterator_element_name, result_name, elem_type);
+ out << ");" << endl;
+ }
+ }
+
+ indent_down();
+
+ indent(out) << "}" << endl;
+ }
+}
+
+void t_java_generator::generate_deep_copy_non_container(ostream& out,
+ std::string source_name,
+ std::string dest_name,
+ t_type* type) {
+ (void)dest_name;
+ type = get_true_type(type);
+ if (type->is_base_type() || type->is_enum() || type->is_typedef()) {
+ if (type->is_binary()) {
+ out << "org.apache.thrift.TBaseHelper.copyBinary(" << source_name << ")";
+ } else {
+ // everything else can be copied directly
+ out << source_name;
+ }
+ } else {
+ out << "new " << type_name(type, true, true) << "(" << source_name << ")";
+ }
+}
+
+std::string t_java_generator::generate_isset_check(t_field* field) {
+ return generate_isset_check(field->get_name());
+}
+
+std::string t_java_generator::isset_field_id(t_field* field) {
+ return "__" + upcase_string(field->get_name() + "_isset_id");
+}
+
+std::string t_java_generator::generate_isset_check(std::string field_name) {
+ return "is" + get_cap_name("set") + get_cap_name(field_name) + "()";
+}
+
+void t_java_generator::generate_isset_set(ostream& out, t_field* field, string prefix) {
+ if (!type_can_be_null(field->get_type())) {
+ indent(out) << prefix << "set" << get_cap_name(field->get_name()) << get_cap_name("isSet")
+ << "(true);" << endl;
+ }
+}
+
+void t_java_generator::generate_struct_desc(ostream& out, t_struct* tstruct) {
+ indent(out) << "private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new "
+ "org.apache.thrift.protocol.TStruct(\"" << tstruct->get_name() << "\");" << endl;
+}
+
+void t_java_generator::generate_field_descs(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "private static final org.apache.thrift.protocol.TField "
+ << constant_name((*m_iter)->get_name())
+ << "_FIELD_DESC = new org.apache.thrift.protocol.TField(\"" << (*m_iter)->get_name()
+ << "\", " << type_to_enum((*m_iter)->get_type()) << ", "
+ << "(short)" << (*m_iter)->get_key() << ");" << endl;
+ }
+}
+
+void t_java_generator::generate_scheme_map(ostream& out, t_struct* tstruct) {
+ indent(out) << "private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new "
+ << tstruct->get_name() << "StandardSchemeFactory();" << endl;
+ indent(out) << "private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new "
+ << tstruct->get_name() << "TupleSchemeFactory();" << endl;
+}
+
+void t_java_generator::generate_field_name_constants(ostream& out, t_struct* tstruct) {
+ indent(out) << "/** The set of fields this struct contains, along with convenience methods for "
+ "finding and manipulating them. */" << endl;
+ indent(out) << "public enum _Fields implements org.apache.thrift.TFieldIdEnum {" << endl;
+
+ indent_up();
+ bool first = true;
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!first) {
+ out << "," << endl;
+ }
+ first = false;
+ generate_java_doc(out, *m_iter);
+ indent(out) << constant_name((*m_iter)->get_name()) << "((short)" << (*m_iter)->get_key()
+ << ", \"" << (*m_iter)->get_name() << "\")";
+ }
+
+ out << ";" << endl << endl;
+
+ indent(out)
+ << "private static final java.util.Map<java.lang.String, _Fields> byName = new java.util.HashMap<java.lang.String, _Fields>();"
+ << endl;
+ out << endl;
+
+ indent(out) << "static {" << endl;
+ indent(out) << " for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) {" << endl;
+ indent(out) << " byName.put(field.getFieldName(), field);" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "/**" << endl;
+ indent(out) << " * Find the _Fields constant that matches fieldId, or null if its not found."
+ << endl;
+ indent(out) << " */" << endl;
+ indent(out) << java_nullable_annotation() << endl;
+ indent(out) << "public static _Fields findByThriftId(int fieldId) {" << endl;
+ indent_up();
+ indent(out) << "switch(fieldId) {" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "case " << (*m_iter)->get_key() << ": // "
+ << constant_name((*m_iter)->get_name()) << endl;
+ indent(out) << " return " << constant_name((*m_iter)->get_name()) << ";" << endl;
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " return null;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "/**" << endl;
+ indent(out) << " * Find the _Fields constant that matches fieldId, throwing an exception" << endl;
+ indent(out) << " * if it is not found." << endl;
+ indent(out) << " */" << endl;
+ indent(out) << "public static _Fields findByThriftIdOrThrow(int fieldId) {" << endl;
+ indent(out) << " _Fields fields = findByThriftId(fieldId);" << endl;
+ indent(out) << " if (fields == null) throw new java.lang.IllegalArgumentException(\"Field \" + fieldId + "
+ "\" doesn't exist!\");" << endl;
+ indent(out) << " return fields;" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "/**" << endl;
+ indent(out) << " * Find the _Fields constant that matches name, or null if its not found."
+ << endl;
+ indent(out) << " */" << endl;
+ indent(out) << java_nullable_annotation() << endl;
+ indent(out) << "public static _Fields findByName(java.lang.String name) {" << endl;
+ indent(out) << " return byName.get(name);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "private final short _thriftId;" << endl;
+ indent(out) << "private final java.lang.String _fieldName;" << endl << endl;
+
+ indent(out) << "_Fields(short thriftId, java.lang.String fieldName) {" << endl;
+ indent(out) << " _thriftId = thriftId;" << endl;
+ indent(out) << " _fieldName = fieldName;" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public short getThriftFieldId() {" << endl;
+ indent(out) << " return _thriftId;" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public java.lang.String getFieldName() {" << endl;
+ indent(out) << " return _fieldName;" << endl;
+ indent(out) << "}" << endl;
+
+ indent_down();
+
+ indent(out) << "}" << endl;
+}
+
+t_java_generator::isset_type t_java_generator::needs_isset(t_struct* tstruct,
+ std::string* outPrimitiveType) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ int count = 0;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!type_can_be_null(get_true_type((*m_iter)->get_type()))) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ return ISSET_NONE;
+ } else if (count <= 64) {
+ if (outPrimitiveType != NULL) {
+ if (count <= 8)
+ *outPrimitiveType = "byte";
+ else if (count <= 16)
+ *outPrimitiveType = "short";
+ else if (count <= 32)
+ *outPrimitiveType = "int";
+ else if (count <= 64)
+ *outPrimitiveType = "long";
+ }
+ return ISSET_PRIMITIVE;
+ } else {
+ return ISSET_BITSET;
+ }
+}
+
+void t_java_generator::generate_java_struct_clear(std::ostream& out, t_struct* tstruct) {
+ if (!java5_) {
+ indent(out) << "@Override" << endl;
+ }
+ indent(out) << "public void clear() {" << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = *m_iter;
+ t_type* t = get_true_type(field->get_type());
+
+ if (field->get_value() != NULL) {
+ print_const_value(out, "this." + field->get_name(), t, field->get_value(), true, true);
+ continue;
+ }
+
+ if (type_can_be_null(t)) {
+
+ if (reuse_objects_ && (t->is_container() || t->is_struct())) {
+ indent(out) << "if (this." << field->get_name() << " != null) {" << endl;
+ indent_up();
+ indent(out) << "this." << field->get_name() << ".clear();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ } else {
+
+ indent(out) << "this." << field->get_name() << " = null;" << endl;
+ }
+ continue;
+ }
+
+ // must be a base type
+ // means it also needs to be explicitly unset
+ indent(out) << "set" << get_cap_name(field->get_name()) << get_cap_name("isSet") << "(false);"
+ << endl;
+ t_base_type* base_type = (t_base_type*)t;
+
+ switch (base_type->get_base()) {
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ indent(out) << "this." << field->get_name() << " = 0;" << endl;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ indent(out) << "this." << field->get_name() << " = 0.0;" << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ indent(out) << "this." << field->get_name() << " = false;" << endl;
+ break;
+ default:
+ throw "unsupported type: " + base_type->get_name() + " for field " + field->get_name();
+ }
+ }
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+// generates java method to serialize (in the Java sense) the object
+void t_java_generator::generate_java_struct_write_object(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out)
+ << "private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {"
+ << endl;
+ indent(out) << " try {" << endl;
+ indent(out) << " write(new org.apache.thrift.protocol.TCompactProtocol(new "
+ "org.apache.thrift.transport.TIOStreamTransport(out)));" << endl;
+ indent(out) << " } catch (org.apache.thrift.TException te) {" << endl;
+ indent(out) << " throw new java.io.IOException(te" << (android_legacy_ ? ".getMessage()" : "")
+ << ");" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl << endl;
+}
+
+// generates java method to serialize (in the Java sense) the object
+void t_java_generator::generate_java_struct_read_object(ostream& out, t_struct* tstruct) {
+ indent(out) << "private void readObject(java.io.ObjectInputStream in) throws "
+ "java.io.IOException, java.lang.ClassNotFoundException {" << endl;
+ indent(out) << " try {" << endl;
+ if (!tstruct->is_union()) {
+ switch (needs_isset(tstruct)) {
+ case ISSET_NONE:
+ break;
+ case ISSET_PRIMITIVE:
+ indent(out) << " // it doesn't seem like you should have to do this, but java "
+ "serialization is wacky, and doesn't call the default constructor." << endl;
+ indent(out) << " __isset_bitfield = 0;" << endl;
+ break;
+ case ISSET_BITSET:
+ indent(out) << " // it doesn't seem like you should have to do this, but java "
+ "serialization is wacky, and doesn't call the default constructor." << endl;
+ indent(out) << " __isset_bit_vector = new java.util.BitSet(1);" << endl;
+ break;
+ }
+ }
+ indent(out) << " read(new org.apache.thrift.protocol.TCompactProtocol(new "
+ "org.apache.thrift.transport.TIOStreamTransport(in)));" << endl;
+ indent(out) << " } catch (org.apache.thrift.TException te) {" << endl;
+ indent(out) << " throw new java.io.IOException(te" << (android_legacy_ ? ".getMessage()" : "")
+ << ");" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl << endl;
+}
+
+void t_java_generator::generate_standard_reader(ostream& out, t_struct* tstruct) {
+ out << indent() << "public void read(org.apache.thrift.protocol.TProtocol iprot, "
+ << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Declare stack tmp variables and read struct header
+ out << indent() << "org.apache.thrift.protocol.TField schemeField;" << endl << indent()
+ << "iprot.readStructBegin();" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true)" << endl;
+ scope_up(out);
+
+ // Read beginning field marker
+ indent(out) << "schemeField = iprot.readFieldBegin();" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { " << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Switch statement on the field we are reading
+ indent(out) << "switch (schemeField.id) {" << endl;
+
+ indent_up();
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << (*f_iter)->get_key() << ": // "
+ << constant_name((*f_iter)->get_name()) << endl;
+ indent_up();
+ indent(out) << "if (schemeField.type == " << type_to_enum((*f_iter)->get_type()) << ") {"
+ << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter, "struct.", true);
+ indent(out) << "struct."
+ << "set" << get_cap_name((*f_iter)->get_name()) << get_cap_name("isSet")
+ << "(true);" << endl;
+ indent_down();
+ out << indent() << "} else { " << endl << indent()
+ << " org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);" << endl
+ << indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);"
+ << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Read field end marker
+ indent(out) << "iprot.readFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ out << indent() << "iprot.readStructEnd();" << endl;
+
+ // in non-beans style, check for required fields of primitive type
+ // (which can be checked here but not in the general validate method)
+ if (!bean_style_) {
+ out << endl << indent() << "// check for required fields of primitive type, which can't be "
+ "checked in the validate method" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) {
+ out << indent() << "if (!struct." << generate_isset_check(*f_iter) << ") {" << endl
+ << indent()
+ << " throw new org.apache.thrift.protocol.TProtocolException(\"Required field '"
+ << (*f_iter)->get_name()
+ << "' was not found in serialized data! Struct: \" + toString());" << endl << indent()
+ << "}" << endl;
+ }
+ }
+ }
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "struct.validate();" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_java_generator::generate_standard_writer(ostream& out, t_struct* tstruct, bool is_result) {
+ indent_up();
+ out << indent() << "public void write(org.apache.thrift.protocol.TProtocol oprot, "
+ << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl;
+ indent_up();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "struct.validate();" << endl << endl;
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ out << indent() << "if (struct." << (*f_iter)->get_name() << " != null) {" << endl;
+ indent_up();
+ }
+ bool optional = ((*f_iter)->get_req() == t_field::T_OPTIONAL) || (is_result && !null_allowed);
+ if (optional) {
+ indent(out) << "if ("
+ << "struct." << generate_isset_check((*f_iter)) << ") {" << endl;
+ indent_up();
+ }
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "struct.", true);
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ if (optional) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ if (null_allowed) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+ // Write the struct map
+ out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();"
+ << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ indent_down();
+}
+
+void t_java_generator::generate_java_struct_standard_scheme(ostream& out,
+ t_struct* tstruct,
+ bool is_result) {
+ indent(out) << "private static class " << tstruct->get_name()
+ << "StandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory {" << endl;
+ indent_up();
+ indent(out) << "public " << tstruct->get_name() << "StandardScheme getScheme() {" << endl;
+ indent_up();
+ indent(out) << "return new " << tstruct->get_name() << "StandardScheme();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ out << indent() << "private static class " << tstruct->get_name()
+ << "StandardScheme extends org.apache.thrift.scheme.StandardScheme<" << tstruct->get_name() << "> {" << endl << endl;
+ indent_up();
+ generate_standard_reader(out, tstruct);
+ indent_down();
+ out << endl;
+ generate_standard_writer(out, tstruct, is_result);
+
+ out << indent() << "}" << endl << endl;
+}
+
+void t_java_generator::generate_java_struct_tuple_reader(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "public void read(org.apache.thrift.protocol.TProtocol prot, "
+ << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl;
+ indent_up();
+ indent(out) << "org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot;" << endl;
+ int optional_count = 0;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL
+ || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) {
+ optional_count++;
+ }
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ generate_deserialize_field(out, (*f_iter), "struct.", false);
+ indent(out) << "struct.set" << get_cap_name((*f_iter)->get_name()) << get_cap_name("isSet")
+ << "(true);" << endl;
+ }
+ }
+ if (optional_count > 0) {
+ indent(out) << "java.util.BitSet incoming = iprot.readBitSet(" << optional_count << ");" << endl;
+ int i = 0;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL
+ || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) {
+ indent(out) << "if (incoming.get(" << i << ")) {" << endl;
+ indent_up();
+ generate_deserialize_field(out, (*f_iter), "struct.", false);
+ indent(out) << "struct.set" << get_cap_name((*f_iter)->get_name()) << get_cap_name("isSet")
+ << "(true);" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ i++;
+ }
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_java_struct_tuple_writer(ostream& out, t_struct* tstruct) {
+ indent(out) << "@Override" << endl;
+ indent(out) << "public void write(org.apache.thrift.protocol.TProtocol prot, "
+ << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl;
+ indent_up();
+ indent(out) << "org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot;" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool has_optional = false;
+ int optional_count = 0;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL
+ || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) {
+ optional_count++;
+ has_optional = true;
+ }
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ generate_serialize_field(out, (*f_iter), "struct.", false);
+ }
+ }
+ if (has_optional) {
+ indent(out) << "java.util.BitSet optionals = new java.util.BitSet();" << endl;
+ int i = 0;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL
+ || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) {
+ indent(out) << "if (struct." << generate_isset_check((*f_iter)) << ") {" << endl;
+ indent_up();
+ indent(out) << "optionals.set(" << i << ");" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ i++;
+ }
+ }
+
+ indent(out) << "oprot.writeBitSet(optionals, " << optional_count << ");" << endl;
+ int j = 0;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_OPTIONAL
+ || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) {
+ indent(out) << "if (struct." << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ generate_serialize_field(out, (*f_iter), "struct.", false);
+ indent_down();
+ indent(out) << "}" << endl;
+ j++;
+ }
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_java_struct_tuple_scheme(ostream& out, t_struct* tstruct) {
+ indent(out) << "private static class " << tstruct->get_name()
+ << "TupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory {" << endl;
+ indent_up();
+ indent(out) << "public " << tstruct->get_name() << "TupleScheme getScheme() {" << endl;
+ indent_up();
+ indent(out) << "return new " << tstruct->get_name() << "TupleScheme();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ out << indent() << "private static class " << tstruct->get_name()
+ << "TupleScheme extends org.apache.thrift.scheme.TupleScheme<" << tstruct->get_name() << "> {" << endl << endl;
+ indent_up();
+ generate_java_struct_tuple_writer(out, tstruct);
+ out << endl;
+ generate_java_struct_tuple_reader(out, tstruct);
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_java_generator::generate_java_scheme_lookup(ostream& out) {
+ indent(out) << "private static <S extends org.apache.thrift.scheme.IScheme> S scheme("
+ << "org.apache.thrift.protocol.TProtocol proto) {" << endl;
+ indent_up();
+ indent(out) << "return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) "
+ << "? STANDARD_SCHEME_FACTORY "
+ << ": TUPLE_SCHEME_FACTORY"
+ << ").getScheme();" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_java_generator::generate_javax_generated_annotation(ostream& out) {
+ time_t seconds = time(NULL);
+ struct tm* now = localtime(&seconds);
+ indent(out) << "@javax.annotation.Generated(value = \"" << autogen_summary() << "\"";
+ if (undated_generated_annotations_) {
+ out << ")" << endl;
+ } else {
+ indent(out) << ", date = \"" << (now->tm_year + 1900) << "-" << setfill('0') << setw(2)
+ << (now->tm_mon + 1) << "-" << setfill('0') << setw(2) << now->tm_mday
+ << "\")" << endl;
+ }
+}
+
+THRIFT_REGISTER_GENERATOR(
+ java,
+ "Java",
+ " beans: Members will be private, and setter methods will return void.\n"
+ " private-members: Members will be private, but setter methods will return 'this' like "
+ "usual.\n"
+ " nocamel: Do not use CamelCase field accessors with beans.\n"
+ " fullcamel: Convert underscored_accessor_or_service_names to camelCase.\n"
+ " android: Generated structures are Parcelable.\n"
+ " android_legacy: Do not use java.io.IOException(throwable) (available for Android 2.3 and "
+ "above).\n"
+ " option_type: Wrap optional fields in an Option type.\n"
+ " rethrow_unhandled_exceptions:\n"
+ " Enable rethrow of unhandled exceptions and let them propagate futher."
+ " (Default behavior is to catch and log it.)\n"
+ " java5: Generate Java 1.5 compliant code (includes android_legacy flag).\n"
+ " reuse-objects: Data objects will not be allocated, but existing instances will be used "
+ "(read and write).\n"
+ " sorted_containers:\n"
+ " Use TreeSet/TreeMap instead of HashSet/HashMap as a implementation of "
+ "set/map.\n"
+ " generated_annotations=[undated|suppress]:\n"
+ " undated: suppress the date at @Generated annotations\n"
+ " suppress: suppress @Generated annotations entirely\n"
+ " unsafe_binaries: Do not copy ByteBuffers in constructors, getters, and setters.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc
new file mode 100644
index 000000000..e1694abdf
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc
@@ -0,0 +1,3294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <sstream>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <sys/stat.h>
+#include <stdexcept>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Java code generator.
+ *
+ */
+class t_javame_generator : public t_oop_generator {
+public:
+ t_javame_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)parsed_options;
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option javame:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-javame";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(std::vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_union(t_struct* tunion);
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ void print_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval = false);
+ std::string render_const_value(std::ostream& out,
+ std::string name,
+ t_type* type,
+ t_const_value* value);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_java_struct(t_struct* tstruct, bool is_exception);
+
+ void generate_java_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool in_class = false,
+ bool is_result = false);
+ void generate_java_struct_equality(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_compare_to(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_java_validator(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_result_writer(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_tostring(std::ostream& out, t_struct* tstruct);
+ void generate_java_struct_clear(std::ostream& out, t_struct* tstruct);
+ void generate_field_value_meta_data(std::ostream& out, t_type* type);
+ std::string get_java_type_string(t_type* type);
+ void generate_reflection_setters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_reflection_getters(std::ostringstream& out,
+ t_type* type,
+ std::string field_name,
+ std::string cap_name);
+ void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct);
+ void generate_java_bean_boilerplate(std::ostream& out, t_struct* tstruct);
+
+ void generate_function_helpers(t_function* tfunction);
+ std::string get_cap_name(std::string name);
+ std::string generate_isset_check(t_field* field);
+ std::string generate_isset_check(std::string field);
+ void generate_isset_set(ostream& out, t_field* field);
+ std::string isset_field_id(t_field* field);
+
+ void generate_primitive_service_interface(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ void generate_java_union(t_struct* tstruct);
+ void generate_union_constructor(ostream& out, t_struct* tstruct);
+ void generate_union_getters_and_setters(ostream& out, t_struct* tstruct);
+ void generate_union_abstract_methods(ostream& out, t_struct* tstruct);
+ void generate_check_type(ostream& out, t_struct* tstruct);
+ void generate_read_value(ostream& out, t_struct* tstruct);
+ void generate_write_value(ostream& out, t_struct* tstruct);
+ void generate_get_field_desc(ostream& out, t_struct* tstruct);
+ void generate_get_struct_desc(ostream& out, t_struct* tstruct);
+ void generate_get_field_name(ostream& out, t_struct* tstruct);
+
+ void generate_union_comparisons(ostream& out, t_struct* tstruct);
+ void generate_union_hashcode(ostream& out, t_struct* tstruct);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string iter,
+ std::string map);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_java_doc(std::ostream& out, t_field* field) override;
+
+ void generate_java_doc(std::ostream& out, t_doc* tdoc) override;
+
+ void generate_java_doc(std::ostream& out, t_function* tdoc) override;
+
+ void generate_java_docstring_comment(std::ostream& out, string contents) override;
+
+ void generate_deep_copy_container(std::ostream& out,
+ std::string source_name_p1,
+ std::string source_name_p2,
+ std::string result_name,
+ t_type* type);
+ void generate_deep_copy_non_container(std::ostream& out,
+ std::string source_name,
+ std::string dest_name,
+ t_type* type);
+
+ bool has_bit_vector(t_struct* tstruct);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string java_package();
+ std::string java_type_imports();
+ std::string java_thrift_imports();
+ std::string type_name(t_type* ttype,
+ bool in_container = false,
+ bool in_init = false,
+ bool skip_generic = false);
+ std::string base_type_name(t_base_type* tbase, bool in_container = false);
+ std::string declare_field(t_field* tfield, bool init = false);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct, bool include_types = true);
+ std::string type_to_enum(t_type* ttype);
+ std::string get_enum_class_name(t_type* type) override;
+ void generate_struct_desc(ostream& out, t_struct* tstruct);
+ void generate_field_descs(ostream& out, t_struct* tstruct);
+ std::string box_type(t_type* type, string value);
+
+ bool type_can_be_null(t_type* ttype) {
+ ttype = get_true_type(ttype);
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()
+ || ttype->is_enum();
+ }
+
+ std::string constant_name(std::string name);
+
+private:
+ /**
+ * File streams
+ */
+
+ std::string package_name_;
+ ofstream_with_content_based_conditional_update f_service_;
+ std::string package_dir_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_javame_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+ package_name_ = program_->get_namespace("java");
+
+ string dir = package_name_;
+ string subdir = get_out_dir();
+ string::size_type loc;
+ while ((loc = dir.find(".")) != string::npos) {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0) {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ package_dir_ = subdir;
+}
+
+/**
+ * Packages the generated file
+ *
+ * @return String of the package, i.e. "package org.apache.thriftdemo;"
+ */
+string t_javame_generator::java_package() {
+ if (!package_name_.empty()) {
+ return string("package ") + package_name_ + ";\n\n";
+ }
+ return "";
+}
+
+/**
+ * Prints standard java imports
+ *
+ * @return List of imports for Java types that are used in here
+ */
+string t_javame_generator::java_type_imports() {
+ return string() + "import java.util.Hashtable;\n" + "import java.util.Vector;\n"
+ + "import java.util.Enumeration;\n\n";
+}
+
+/**
+ * Prints standard java imports
+ *
+ * @return List of imports necessary for thrift
+ */
+string t_javame_generator::java_thrift_imports() {
+ return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n"
+ + "import org.apache.thrift.transport.*;\n" + "import org.apache.thrift.protocol.*;\n\n";
+}
+
+/**
+ * Nothing in Java
+ */
+void t_javame_generator::close_generator() {
+}
+
+/**
+ * Generates a typedef. This is not done in Java, since it does
+ * not support arbitrary name replacements, and it'd be a wacky waste
+ * of overhead to make wrapper classes.
+ *
+ * @param ttypedef The type definition
+ */
+void t_javame_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Enums are a class with a set of static constants.
+ *
+ * @param tenum The enumeration
+ */
+void t_javame_generator::generate_enum(t_enum* tenum) {
+ // Make output file
+ string f_enum_name = package_dir_ + "/" + (tenum->get_name()) + ".java";
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ // Comment and package it
+ f_enum << autogen_comment() << java_package();
+
+ generate_java_doc(f_enum, tenum);
+ indent(f_enum) << "public class " << tenum->get_name() << " implements org.apache.thrift.TEnum ";
+ scope_up(f_enum);
+ f_enum << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ generate_java_doc(f_enum, *c_iter);
+ indent(f_enum) << "public static final " << tenum->get_name() << " " << (*c_iter)->get_name()
+ << " = new " << tenum->get_name() << "(" << value << ");" << endl;
+ }
+ f_enum << endl;
+
+ // Field for thriftCode
+ indent(f_enum) << "private final int value;" << endl << endl;
+
+ indent(f_enum) << "private " << tenum->get_name() << "(int value) {" << endl;
+ indent(f_enum) << " this.value = value;" << endl;
+ indent(f_enum) << "}" << endl << endl;
+
+ indent(f_enum) << "/**" << endl;
+ indent(f_enum) << " * Get the integer value of this enum value, as defined in the Thrift IDL."
+ << endl;
+ indent(f_enum) << " */" << endl;
+ indent(f_enum) << "public int getValue() {" << endl;
+ indent(f_enum) << " return value;" << endl;
+ indent(f_enum) << "}" << endl << endl;
+
+ indent(f_enum) << "/**" << endl;
+ indent(f_enum) << " * Find a the enum type by its integer value, as defined in the Thrift IDL."
+ << endl;
+ indent(f_enum) << " * @return null if the value is not found." << endl;
+ indent(f_enum) << " */" << endl;
+ indent(f_enum) << "public static " + tenum->get_name() + " findByValue(int value) { " << endl;
+
+ indent_up();
+
+ indent(f_enum) << "switch (value) {" << endl;
+ indent_up();
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << "case " << value << ":" << endl;
+ indent(f_enum) << " return " << (*c_iter)->get_name() << ";" << endl;
+ }
+
+ indent(f_enum) << "default:" << endl;
+ indent(f_enum) << " return null;" << endl;
+
+ indent_down();
+
+ indent(f_enum) << "}" << endl;
+
+ indent_down();
+
+ indent(f_enum) << "}" << endl;
+
+ scope_down(f_enum);
+
+ f_enum.close();
+}
+
+/**
+ * Generates a class that holds all the constants.
+ */
+void t_javame_generator::generate_consts(std::vector<t_const*> consts) {
+ if (consts.empty()) {
+ return;
+ }
+
+ string f_consts_name = package_dir_ + "/" + program_name_ + "Constants.java";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ // Print header
+ f_consts << autogen_comment() << java_package() << java_type_imports();
+
+ f_consts << "public class " << program_name_ << "Constants {" << endl << endl;
+ indent_up();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ print_const_value(f_consts,
+ (*c_iter)->get_name(),
+ (*c_iter)->get_type(),
+ (*c_iter)->get_value(),
+ false);
+ }
+ indent_down();
+ indent(f_consts) << "}" << endl;
+ f_consts.close();
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+void t_javame_generator::print_const_value(std::ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value,
+ bool in_static,
+ bool defval) {
+ type = get_true_type(type);
+
+ indent(out);
+ if (!defval) {
+ out << (in_static ? "" : "public static final ") << type_name(type) << " ";
+ }
+ if (type->is_base_type()) {
+ string v2 = render_const_value(out, name, type, value);
+ out << name << " = " << v2 << ";" << endl << endl;
+ } else if (type->is_enum()) {
+ out << name << " = " << render_const_value(out, name, type, value) << ";" << endl << endl;
+ } else if (type->is_struct() || type->is_xception()) {
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ out << name << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "static {" << endl;
+ indent_up();
+ }
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ indent(out) << name << ".";
+ std::string cap_name = get_cap_name(v_iter->first->get_string());
+ out << "set" << cap_name << "(" << val << ");" << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_map()) {
+ out << name << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "static {" << endl;
+ indent_up();
+ }
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ indent(out) << name << ".put(" << box_type(ktype, key) << ", " << box_type(vtype, val) << ");"
+ << endl;
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else if (type->is_list() || type->is_set()) {
+ out << name << " = new " << type_name(type, false, true) << "();" << endl;
+ if (!in_static) {
+ indent(out) << "static {" << endl;
+ indent_up();
+ }
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(out, name, etype, *v_iter);
+ if (type->is_list()) {
+ indent(out) << name << ".addElement(" << box_type(etype, val) << ");" << endl;
+ } else {
+ indent(out) << name << ".put(" << box_type(etype, val) << ", " << box_type(etype, val)
+ << ");" << endl;
+ }
+ }
+ if (!in_static) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ out << endl;
+ } else {
+ throw "compiler error: no const of type " + type->get_name();
+ }
+}
+
+string t_javame_generator::render_const_value(ostream& out,
+ string name,
+ t_type* type,
+ t_const_value* value) {
+ (void)name;
+ type = get_true_type(type);
+ std::ostringstream render;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ render << "(byte)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I16:
+ render << "(short)" << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ render << value->get_integer() << "L";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ render << "(double)" << value->get_integer();
+ } else {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ render << type_name(type, false, false) << "." << value->get_identifier();
+ } else {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+string t_javame_generator::box_type(t_type* type, string value) {
+ if (type->is_base_type()) {
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_BOOL:
+ return "new Boolean(" + value + ")";
+ case t_base_type::TYPE_I8:
+ return "new Byte(" + value + ")";
+ case t_base_type::TYPE_I16:
+ return "new Short(" + value + ")";
+ case t_base_type::TYPE_I32:
+ return "new Integer(" + value + ")";
+ case t_base_type::TYPE_I64:
+ return "new Long(" + value + ")";
+ case t_base_type::TYPE_DOUBLE:
+ return "new Double(" + value + ")";
+ default:
+ break;
+ }
+ }
+ return value;
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This will be a TBase
+ * implementor.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_struct(t_struct* tstruct) {
+ if (tstruct->is_union()) {
+ generate_java_union(tstruct);
+ } else {
+ generate_java_struct(tstruct, false);
+ }
+}
+
+/**
+ * Exceptions are structs, but they inherit from Exception
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_xception(t_struct* txception) {
+ generate_java_struct(txception, true);
+}
+
+/**
+ * Java struct definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_struct(t_struct* tstruct, bool is_exception) {
+ // Make output file
+ string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".java";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports();
+
+ generate_java_struct_definition(f_struct, tstruct, is_exception);
+ f_struct.close();
+}
+
+/**
+ * Java union definition.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_union(t_struct* tstruct) {
+ // Make output file
+ string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".java";
+ ofstream_with_content_based_conditional_update f_struct;
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports();
+
+ generate_java_doc(f_struct, tstruct);
+
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+
+ indent(f_struct) << "public " << (is_final ? "final " : "") << "class " << tstruct->get_name()
+ << " extends TUnion ";
+
+ scope_up(f_struct);
+
+ generate_struct_desc(f_struct, tstruct);
+ generate_field_descs(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_constructor(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_abstract_methods(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_getters_and_setters(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_comparisons(f_struct, tstruct);
+
+ f_struct << endl;
+
+ generate_union_hashcode(f_struct, tstruct);
+
+ f_struct << endl;
+
+ scope_down(f_struct);
+
+ f_struct.close();
+}
+
+void t_javame_generator::generate_union_constructor(ostream& out, t_struct* tstruct) {
+ indent(out) << "public " << type_name(tstruct) << "() {" << endl;
+ indent(out) << " super();" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public " << type_name(tstruct) << "(_Fields setField, Object value) {" << endl;
+ indent(out) << " super(setField, value);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public " << type_name(tstruct) << "(" << type_name(tstruct) << " other) {"
+ << endl;
+ indent(out) << " super(other);" << endl;
+ indent(out) << "}" << endl;
+
+ indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl;
+ indent(out) << " return new " << tstruct->get_name() << "(this);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ // generate "constructors" for each field
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "public static " << type_name(tstruct) << " " << (*m_iter)->get_name() << "("
+ << type_name((*m_iter)->get_type()) << " value) {" << endl;
+ indent(out) << " " << type_name(tstruct) << " x = new " << type_name(tstruct) << "();" << endl;
+ indent(out) << " x.set" << get_cap_name((*m_iter)->get_name()) << "(value);" << endl;
+ indent(out) << " return x;" << endl;
+ indent(out) << "}" << endl << endl;
+ }
+}
+
+void t_javame_generator::generate_union_getters_and_setters(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << endl;
+ }
+
+ t_field* field = (*m_iter);
+
+ generate_java_doc(out, field);
+ indent(out) << "public " << type_name(field->get_type()) << " get"
+ << get_cap_name(field->get_name()) << "() {" << endl;
+ indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {"
+ << endl;
+ indent(out) << " return (" << type_name(field->get_type(), true) << ")getFieldValue();"
+ << endl;
+ indent(out) << " } else {" << endl;
+ indent(out) << " throw new RuntimeException(\"Cannot get field '" << field->get_name()
+ << "' because union is currently set to \" + getFieldDesc(getSetField()).name);"
+ << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+
+ out << endl;
+
+ generate_java_doc(out, field);
+ indent(out) << "public void set" << get_cap_name(field->get_name()) << "("
+ << type_name(field->get_type()) << " value) {" << endl;
+ if (type_can_be_null(field->get_type())) {
+ indent(out) << " if (value == null) throw new NullPointerException();" << endl;
+ }
+ indent(out) << " setField_ = _Fields." << constant_name(field->get_name()) << ";" << endl;
+ indent(out) << " value_ = value;" << endl;
+ indent(out) << "}" << endl;
+ }
+}
+
+void t_javame_generator::generate_union_abstract_methods(ostream& out, t_struct* tstruct) {
+ generate_check_type(out, tstruct);
+ out << endl;
+ generate_read_value(out, tstruct);
+ out << endl;
+ generate_write_value(out, tstruct);
+ out << endl;
+ generate_get_field_desc(out, tstruct);
+ out << endl;
+ generate_get_struct_desc(out, tstruct);
+ out << endl;
+}
+
+void t_javame_generator::generate_check_type(ostream& out, t_struct* tstruct) {
+ indent(out)
+ << "protected void checkType(_Fields setField, Object value) throws ClassCastException {"
+ << endl;
+ indent_up();
+
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent(out) << " if (value instanceof " << type_name(field->get_type(), true, false, true)
+ << ") {" << endl;
+ indent(out) << " break;" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << " throw new ClassCastException(\"Was expecting value of type "
+ << type_name(field->get_type(), true, false) << " for field '" << field->get_name()
+ << "', but got \" + value.getClass().getSimpleName());" << endl;
+ // do the real check here
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new IllegalArgumentException(\"Unknown field id \" + setField);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_javame_generator::generate_read_value(ostream& out, t_struct* tstruct) {
+ indent(out) << "protected Object readValue(TProtocol iprot, TField field) throws TException {"
+ << endl;
+
+ indent_up();
+
+ indent(out) << "_Fields setField = _Fields.findByThriftId(field.id);" << endl;
+ indent(out) << "if (setField != null) {" << endl;
+ indent_up();
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << "if (field.type == " << constant_name(field->get_name()) << "_FIELD_DESC.type) {"
+ << endl;
+ indent_up();
+ indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << ";"
+ << endl;
+ generate_deserialize_field(out, field, "");
+ indent(out) << "return " << field->get_name() << ";" << endl;
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent(out) << " TProtocolUtil.skip(iprot, field.type);" << endl;
+ indent(out) << " return null;" << endl;
+ indent(out) << "}" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new IllegalStateException(\"setField wasn't null, but didn't match any "
+ "of the case statements!\");" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "} else {" << endl;
+ indent_up();
+ indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl;
+ indent(out) << "return null;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_javame_generator::generate_write_value(ostream& out, t_struct* tstruct) {
+ indent(out) << "protected void writeValue(TProtocol oprot) throws TException {" << endl;
+
+ indent_up();
+
+ indent(out) << "switch (setField_) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent_up();
+ indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << " = ("
+ << type_name(field->get_type(), true, false) << ")value_;" << endl;
+ generate_serialize_field(out, field, "");
+ indent(out) << "return;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new IllegalStateException(\"Cannot write union with unknown field \" + "
+ "setField_);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+
+ indent(out) << "}" << endl;
+}
+
+void t_javame_generator::generate_get_field_desc(ostream& out, t_struct* tstruct) {
+ indent(out) << "protected TField getFieldDesc(_Fields setField) {" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ indent(out) << "switch (setField) {" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+ indent(out) << "case " << constant_name(field->get_name()) << ":" << endl;
+ indent(out) << " return " << constant_name(field->get_name()) << "_FIELD_DESC;" << endl;
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " throw new IllegalArgumentException(\"Unknown field id \" + setField);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_javame_generator::generate_get_struct_desc(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "protected TStruct getStructDesc() {" << endl;
+ indent(out) << " return STRUCT_DESC;" << endl;
+ indent(out) << "}" << endl;
+}
+
+void t_javame_generator::generate_union_comparisons(ostream& out, t_struct* tstruct) {
+ // equality
+ indent(out) << "public boolean equals(Object other) {" << endl;
+ indent(out) << " if (other instanceof " << tstruct->get_name() << ") {" << endl;
+ indent(out) << " return equals((" << tstruct->get_name() << ")other);" << endl;
+ indent(out) << " } else {" << endl;
+ indent(out) << " return false;" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+
+ out << endl;
+
+ indent(out) << "public boolean equals(" << tstruct->get_name() << " other) {" << endl;
+ indent(out) << " return other != null && getSetField() == other.getSetField() && "
+ "getFieldValue().equals(other.getFieldValue());" << endl;
+ indent(out) << "}" << endl;
+ out << endl;
+
+ indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl;
+ indent(out) << " int lastComparison = TBaseHelper.compareTo(getSetField(), other.getSetField());"
+ << endl;
+ indent(out) << " if (lastComparison == 0) {" << endl;
+ indent(out) << " return TBaseHelper.compareTo(getFieldValue(), other.getFieldValue());"
+ << endl;
+ indent(out) << " }" << endl;
+ indent(out) << " return lastComparison;" << endl;
+ indent(out) << "}" << endl;
+ out << endl;
+}
+
+void t_javame_generator::generate_union_hashcode(ostream& out, t_struct* tstruct) {
+ (void)tstruct;
+ indent(out) << "/**" << endl;
+ indent(out)
+ << " * If you'd like this to perform more respectably, use the hashcode generator option."
+ << endl;
+ indent(out) << " */" << endl;
+ indent(out) << "public int hashCode() {" << endl;
+ indent(out) << " return 0;" << endl;
+ indent(out) << "}" << endl;
+}
+
+/**
+ * Java struct definition. This has various parameters, as it could be
+ * generated standalone or inside another class as a helper. If it
+ * is a helper than it is a static class.
+ *
+ * @param tstruct The struct definition
+ * @param is_exception Is this an exception?
+ * @param in_class If inside a class, needs to be static class
+ * @param is_result If this is a result it needs a different writer
+ */
+void t_javame_generator::generate_java_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool in_class,
+ bool is_result) {
+ generate_java_doc(out, tstruct);
+
+ bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
+
+ indent(out) << "public " << (is_final ? "final " : "") << (in_class ? "static " : "") << "class "
+ << tstruct->get_name() << " ";
+
+ if (is_exception) {
+ out << "extends Exception ";
+ }
+ out << "implements TBase ";
+
+ scope_up(out);
+
+ generate_struct_desc(out, tstruct);
+
+ // Members are public for -java, private for -javabean
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << endl;
+
+ generate_field_descs(out, tstruct);
+
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "private ";
+ out << declare_field(*m_iter, false) << endl;
+ }
+
+ // isset data
+ if (members.size() > 0) {
+ out << endl;
+
+ indent(out) << "// isset id assignments" << endl;
+
+ int i = 0;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!type_can_be_null((*m_iter)->get_type())) {
+ indent(out) << "private static final int " << isset_field_id(*m_iter) << " = " << i << ";"
+ << endl;
+ i++;
+ }
+ }
+
+ if (i > 0) {
+ indent(out) << "private boolean[] __isset_vector = new boolean[" << i << "];" << endl;
+ }
+
+ out << endl;
+ }
+
+ bool all_optional_members = true;
+
+ // Default constructor
+ indent(out) << "public " << tstruct->get_name() << "() {" << endl;
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL) {
+ print_const_value(out,
+ "this." + (*m_iter)->get_name(),
+ t,
+ (*m_iter)->get_value(),
+ true,
+ true);
+ }
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ all_optional_members = false;
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ if (!members.empty() && !all_optional_members) {
+ // Full constructor for all fields
+ indent(out) << "public " << tstruct->get_name() << "(" << endl;
+ indent_up();
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ if (!first) {
+ out << "," << endl;
+ }
+ first = false;
+ indent(out) << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name();
+ }
+ }
+ out << ")" << endl;
+ indent_down();
+ indent(out) << "{" << endl;
+ indent_up();
+ indent(out) << "this();" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
+ indent(out) << "this." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << ";"
+ << endl;
+ generate_isset_set(out, (*m_iter));
+ }
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ // copy constructor
+ indent(out) << "/**" << endl;
+ indent(out) << " * Performs a deep copy on <i>other</i>." << endl;
+ indent(out) << " */" << endl;
+ indent(out) << "public " << tstruct->get_name() << "(" << tstruct->get_name() << " other) {"
+ << endl;
+ indent_up();
+
+ if (has_bit_vector(tstruct)) {
+ indent(out) << "System.arraycopy(other.__isset_vector, 0, __isset_vector, 0, "
+ "other.__isset_vector.length);" << endl;
+ }
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = (*m_iter);
+ std::string field_name = field->get_name();
+ t_type* type = field->get_type();
+ bool can_be_null = type_can_be_null(type);
+
+ if (can_be_null) {
+ indent(out) << "if (other." << generate_isset_check(field) << ") {" << endl;
+ indent_up();
+ }
+
+ if (type->is_container()) {
+ generate_deep_copy_container(out, "other", field_name, "__this__" + field_name, type);
+ indent(out) << "this." << field_name << " = __this__" << field_name << ";" << endl;
+ } else {
+ indent(out) << "this." << field_name << " = ";
+ generate_deep_copy_non_container(out, "other." + field_name, field_name, type);
+ out << ";" << endl;
+ }
+
+ if (can_be_null) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // clone method, so that you can deep copy an object when you don't know its class.
+ indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl;
+ indent(out) << " return new " << tstruct->get_name() << "(this);" << endl;
+ indent(out) << "}" << endl << endl;
+
+ generate_java_struct_clear(out, tstruct);
+
+ generate_java_bean_boilerplate(out, tstruct);
+ generate_generic_field_getters_setters(out, tstruct);
+
+ generate_java_struct_equality(out, tstruct);
+ generate_java_struct_compare_to(out, tstruct);
+
+ generate_java_struct_reader(out, tstruct);
+ if (is_result) {
+ generate_java_struct_result_writer(out, tstruct);
+ } else {
+ generate_java_struct_writer(out, tstruct);
+ }
+ generate_java_struct_tostring(out, tstruct);
+ generate_java_validator(out, tstruct);
+ scope_down(out);
+ out << endl;
+}
+
+/**
+ * Generates equals methods and a hashCode method for a structure.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_struct_equality(ostream& out, t_struct* tstruct) {
+ out << indent() << "public boolean equals(Object that) {" << endl;
+ indent_up();
+ out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl
+ << indent() << "if (that instanceof " << tstruct->get_name() << ")" << endl << indent()
+ << " return this.equals((" << tstruct->get_name() << ")that);" << endl << indent()
+ << "return false;" << endl;
+ scope_down(out);
+ out << endl;
+
+ out << indent() << "public boolean equals(" << tstruct->get_name() << " that) {" << endl;
+ indent_up();
+ out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl
+ << indent() << "if (this == that)" << endl << indent() << " return true;" << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << endl;
+
+ t_type* t = get_true_type((*m_iter)->get_type());
+ // Most existing Thrift code does not use isset or optional/required,
+ // so we treat "default" fields as required.
+ bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL;
+ bool can_be_null = type_can_be_null(t);
+ string name = (*m_iter)->get_name();
+
+ string this_present = "true";
+ string that_present = "true";
+ string unequal;
+
+ if (is_optional || can_be_null) {
+ this_present += " && this." + generate_isset_check(*m_iter);
+ that_present += " && that." + generate_isset_check(*m_iter);
+ }
+
+ out << indent() << "boolean this_present_" << name << " = " << this_present << ";" << endl
+ << indent() << "boolean that_present_" << name << " = " << that_present << ";" << endl
+ << indent() << "if ("
+ << "this_present_" << name << " || that_present_" << name << ") {" << endl;
+ indent_up();
+ out << indent() << "if (!("
+ << "this_present_" << name << " && that_present_" << name << "))" << endl << indent()
+ << " return false;" << endl;
+
+ if (t->is_binary()) {
+ unequal = "TBaseHelper.compareTo(this." + name + ", that." + name + ") != 0";
+ } else if (can_be_null) {
+ unequal = "!this." + name + ".equals(that." + name + ")";
+ } else {
+ unequal = "this." + name + " != that." + name;
+ }
+
+ out << indent() << "if (" << unequal << ")" << endl << indent() << " return false;" << endl;
+
+ scope_down(out);
+ }
+ out << endl;
+ indent(out) << "return true;" << endl;
+ scope_down(out);
+ out << endl;
+
+ out << indent() << "public int hashCode() {" << endl;
+ indent_up();
+ indent(out) << "return 0;" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+void t_javame_generator::generate_java_struct_compare_to(ostream& out, t_struct* tstruct) {
+ indent(out) << "public int compareTo(Object otherObject) {" << endl;
+ // indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl;
+ indent_up();
+
+ indent(out) << "if (!getClass().equals(otherObject.getClass())) {" << endl;
+ indent(out) << " return getClass().getName().compareTo(otherObject.getClass().getName());"
+ << endl;
+ indent(out) << "}" << endl;
+ out << endl;
+ indent(out) << type_name(tstruct) << " other = (" << type_name(tstruct) << ")otherObject;";
+
+ indent(out) << "int lastComparison = 0;" << endl;
+ out << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* field = *m_iter;
+ indent(out) << "lastComparison = TBaseHelper.compareTo(" << generate_isset_check(field)
+ << ", other." << generate_isset_check(field) << ");" << endl;
+ indent(out) << "if (lastComparison != 0) {" << endl;
+ indent(out) << " return lastComparison;" << endl;
+ indent(out) << "}" << endl;
+
+ indent(out) << "if (" << generate_isset_check(field) << ") {" << endl;
+ if (field->get_type()->is_struct() || field->get_type()->is_xception()) {
+ indent(out) << " lastComparison = this." << field->get_name() << ".compareTo(other."
+ << field->get_name() << ");" << endl;
+ } else {
+ indent(out) << " lastComparison = TBaseHelper.compareTo(this." << field->get_name()
+ << ", other." << field->get_name() << ");" << endl;
+ }
+
+ indent(out) << " if (lastComparison != 0) {" << endl;
+ indent(out) << " return lastComparison;" << endl;
+ indent(out) << " }" << endl;
+ indent(out) << "}" << endl;
+ }
+
+ indent(out) << "return 0;" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to read all the fields of the struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_struct_reader(ostream& out, t_struct* tstruct) {
+ out << indent() << "public void read(TProtocol iprot) throws TException {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Declare stack tmp variables and read struct header
+ out << indent() << "TField field;" << endl << indent() << "iprot.readStructBegin();" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true)" << endl;
+ scope_up(out);
+
+ // Read beginning field marker
+ indent(out) << "field = iprot.readFieldBegin();" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if (field.type == TType.STOP) { " << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Switch statement on the field we are reading
+ indent(out) << "switch (field.id) {" << endl;
+
+ indent_up();
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << (*f_iter)->get_key() << ": // "
+ << constant_name((*f_iter)->get_name()) << endl;
+ indent_up();
+ indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter, "this.");
+ generate_isset_set(out, *f_iter);
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " TProtocolUtil.skip(iprot, field.type);"
+ << endl << indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ indent(out) << "default:" << endl;
+ indent(out) << " TProtocolUtil.skip(iprot, field.type);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Read field end marker
+ indent(out) << "iprot.readFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ out << indent() << "iprot.readStructEnd();" << endl;
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+// generates java method to perform various checks
+// (e.g. check that all required fields are set)
+void t_javame_generator::generate_java_validator(ostream& out, t_struct* tstruct) {
+ indent(out) << "public void validate() throws TException {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "// check for required fields" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
+ out << indent() << "if (!" << generate_isset_check(*f_iter) << ") {" << endl << indent()
+ << " throw new TProtocolException(\"Required field '" << (*f_iter)->get_name()
+ << "' is unset! Struct:\" + toString());" << endl << indent() << "}" << endl << endl;
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_struct_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public void write(TProtocol oprot) throws TException {" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "validate();" << endl << endl;
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed) {
+ out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl;
+ indent_up();
+ }
+ bool optional = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (optional) {
+ indent(out) << "if (" << generate_isset_check((*f_iter)) << ") {" << endl;
+ indent_up();
+ }
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ if (optional) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ if (null_allowed) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+ // Write the struct map
+ out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();"
+ << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates a function to write all the fields of the struct,
+ * which is a function result. These fields are only written
+ * if they are set in the Isset array, and only one of them
+ * can be set at a time.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_struct_result_writer(ostream& out, t_struct* tstruct) {
+ out << indent() << "public void write(TProtocol oprot) throws TException {" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl;
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ out << endl << indent() << "if ";
+ } else {
+ out << " else if ";
+ }
+
+ out << "(this." << generate_isset_check(*f_iter) << ") {" << endl;
+
+ indent_up();
+
+ indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name())
+ << "_FIELD_DESC);" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}";
+ }
+ // Write the struct map
+ out << endl << indent() << "oprot.writeFieldStop();" << endl << indent()
+ << "oprot.writeStructEnd();" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_javame_generator::generate_reflection_getters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ indent(out) << "case " << constant_name(field_name) << ":" << endl;
+ indent_up();
+
+ if (type->is_base_type() && !type->is_string()) {
+ t_base_type* base_type = (t_base_type*)type;
+
+ indent(out) << "return new " << type_name(type, true, false) << "("
+ << (base_type->is_bool() ? "is" : "get") << cap_name << "());" << endl << endl;
+ } else {
+ indent(out) << "return get" << cap_name << "();" << endl << endl;
+ }
+
+ indent_down();
+}
+
+void t_javame_generator::generate_reflection_setters(ostringstream& out,
+ t_type* type,
+ string field_name,
+ string cap_name) {
+ indent(out) << "case " << constant_name(field_name) << ":" << endl;
+ indent_up();
+ indent(out) << "if (value == null) {" << endl;
+ indent(out) << " unset" << get_cap_name(field_name) << "();" << endl;
+ indent(out) << "} else {" << endl;
+ indent(out) << " set" << cap_name << "((" << type_name(type, true, false) << ")value);" << endl;
+ indent(out) << "}" << endl;
+ indent(out) << "break;" << endl << endl;
+
+ indent_down();
+}
+
+void t_javame_generator::generate_generic_field_getters_setters(std::ostream& out,
+ t_struct* tstruct) {
+ (void)out;
+ std::ostringstream getter_stream;
+ std::ostringstream setter_stream;
+
+ // build up the bodies of both the getter and setter at once
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ indent_up();
+ generate_reflection_setters(setter_stream, type, field_name, cap_name);
+ generate_reflection_getters(getter_stream, type, field_name, cap_name);
+ indent_down();
+ }
+}
+
+/**
+ * Generates a set of Java Bean boilerplate functions (setters, getters, etc.)
+ * for the given struct.
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_bean_boilerplate(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = *f_iter;
+ t_type* type = get_true_type(field->get_type());
+ std::string field_name = field->get_name();
+ std::string cap_name = get_cap_name(field_name);
+
+ if (type->is_container()) {
+ // Method to return the size of the collection
+ indent(out) << "public int get" << cap_name;
+ out << get_cap_name("size() {") << endl;
+
+ indent_up();
+ indent(out) << "return (this." << field_name << " == null) ? 0 : "
+ << "this." << field_name << ".size();" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ if (type->is_set() || type->is_list()) {
+
+ t_type* element_type;
+ if (type->is_set()) {
+ element_type = ((t_set*)type)->get_elem_type();
+ } else {
+ element_type = ((t_list*)type)->get_elem_type();
+ }
+
+ // Iterator getter for sets and lists
+ indent(out) << "public Enumeration get" << cap_name;
+ out << get_cap_name("Enumeration() {") << endl;
+
+ indent_up();
+ indent(out) << "return (this." << field_name << " == null) ? null : "
+ << "this." << field_name << ".elements();" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Add to set or list, create if the set/list is null
+ indent(out);
+ out << "public void add" << get_cap_name("to");
+ out << cap_name << "(" << type_name(element_type) << " elem) {" << endl;
+
+ indent_up();
+ indent(out) << "if (this." << field_name << " == null) {" << endl;
+ indent_up();
+ indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "();"
+ << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ if (type->is_set()) {
+ indent(out) << "this." << field_name << ".put(" << box_type(element_type, "elem") << ", "
+ << box_type(element_type, "elem") << ");" << endl;
+ } else {
+ indent(out) << "this." << field_name << ".addElement(" << box_type(element_type, "elem")
+ << ");" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ } else if (type->is_map()) {
+ // Put to map
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+
+ indent(out);
+ out << "public void putTo" << cap_name << "(" << type_name(key_type, true) << " key, "
+ << type_name(val_type, true) << " val) {" << endl;
+
+ indent_up();
+ indent(out) << "if (this." << field_name << " == null) {" << endl;
+ indent_up();
+ indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "();"
+ << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ indent(out) << "this." << field_name << ".put(key, val);" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+
+ // Simple getter
+ generate_java_doc(out, field);
+ indent(out) << "public " << type_name(type);
+ if (type->is_base_type() && ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) {
+ out << " is";
+ } else {
+ out << " get";
+ }
+ out << cap_name << "() {" << endl;
+ indent_up();
+ indent(out) << "return this." << field_name << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Simple setter
+ generate_java_doc(out, field);
+ indent(out) << "public ";
+ out << "void";
+ out << " set" << cap_name << "(" << type_name(type) << " " << field_name << ") {" << endl;
+ indent_up();
+ indent(out) << "this." << field_name << " = " << field_name << ";" << endl;
+ generate_isset_set(out, field);
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // Unsetter
+ indent(out) << "public void unset" << cap_name << "() {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "this." << field_name << " = null;" << endl;
+ } else {
+ indent(out) << "__isset_vector[" << isset_field_id(field) << "] = false;" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ // isSet method
+ indent(out) << "/** Returns true if field " << field_name
+ << " is set (has been assigned a value) and false otherwise */" << endl;
+ indent(out) << "public boolean is" << get_cap_name("set") << cap_name << "() {" << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "return this." << field_name << " != null;" << endl;
+ } else {
+ indent(out) << "return __isset_vector[" << isset_field_id(field) << "];" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+
+ indent(out) << "public void set" << cap_name << get_cap_name("isSet") << "(boolean value) {"
+ << endl;
+ indent_up();
+ if (type_can_be_null(type)) {
+ indent(out) << "if (!value) {" << endl;
+ indent(out) << " this." << field_name << " = null;" << endl;
+ indent(out) << "}" << endl;
+ } else {
+ indent(out) << "__isset_vector[" << isset_field_id(field) << "] = value;" << endl;
+ }
+ indent_down();
+ indent(out) << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a toString() method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_javame_generator::generate_java_struct_tostring(ostream& out, t_struct* tstruct) {
+ out << indent() << "public String toString() {" << endl;
+ indent_up();
+
+ out << indent() << "StringBuffer sb = new StringBuffer(\"" << tstruct->get_name() << "(\");"
+ << endl;
+ out << indent() << "boolean first = true;" << endl << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL;
+ if (could_be_unset) {
+ indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl;
+ indent_up();
+ }
+
+ t_field* field = (*f_iter);
+
+ if (!first) {
+ indent(out) << "if (!first) sb.append(\", \");" << endl;
+ }
+ indent(out) << "sb.append(\"" << (*f_iter)->get_name() << ":\");" << endl;
+ bool can_be_null = type_can_be_null(field->get_type());
+ if (can_be_null) {
+ indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl;
+ indent(out) << " sb.append(\"null\");" << endl;
+ indent(out) << "} else {" << endl;
+ indent_up();
+ }
+
+ if (field->get_type()->is_binary()) {
+ indent(out) << "TBaseHelper.toString(this." << field->get_name() << ", sb);" << endl;
+ } else {
+ indent(out) << "sb.append(this." << (*f_iter)->get_name() << ");" << endl;
+ }
+
+ if (can_be_null) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ indent(out) << "first = false;" << endl;
+
+ if (could_be_unset) {
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ first = false;
+ }
+ out << indent() << "sb.append(\")\");" << endl << indent() << "return sb.toString();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl << endl;
+}
+
+/**
+ * Returns a string with the java representation of the given thrift type
+ * (e.g. for the type struct it returns "TType.STRUCT")
+ */
+std::string t_javame_generator::get_java_type_string(t_type* type) {
+ if (type->is_list()) {
+ return "TType.LIST";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_enum()) {
+ return "TType.ENUM";
+ } else if (type->is_typedef()) {
+ return get_java_type_string(((t_typedef*)type)->get_type());
+ } else if (type->is_base_type()) {
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "TType.VOID";
+ break;
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ break;
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ break;
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ break;
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ break;
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ break;
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ break;
+ default:
+ throw std::runtime_error("Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_javame_generator::get_java_type_string!");
+ break; // This should never happen!
+ }
+ } else {
+ throw std::runtime_error(
+ "Unknown thrift type \"" + type->get_name()
+ + "\" passed to t_javame_generator::get_java_type_string!"); // This should never happen!
+ }
+}
+
+void t_javame_generator::generate_field_value_meta_data(std::ostream& out, t_type* type) {
+ out << endl;
+ indent_up();
+ indent_up();
+ if (type->is_struct() || type->is_xception()) {
+ indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type) << ".class";
+ } else if (type->is_container()) {
+ if (type->is_list()) {
+ indent(out) << "new ListMetaData(TType.LIST, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else if (type->is_set()) {
+ indent(out) << "new SetMetaData(TType.SET, ";
+ t_type* elem_type = ((t_list*)type)->get_elem_type();
+ generate_field_value_meta_data(out, elem_type);
+ } else { // map
+ indent(out) << "new MapMetaData(TType.MAP, ";
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ t_type* val_type = ((t_map*)type)->get_val_type();
+ generate_field_value_meta_data(out, key_type);
+ out << ", ";
+ generate_field_value_meta_data(out, val_type);
+ }
+ } else if (type->is_enum()) {
+ indent(out) << "new EnumMetaData(TType.ENUM, " << type_name(type) << ".class";
+ } else {
+ indent(out) << "new FieldValueMetaData(" << get_java_type_string(type);
+ if (type->is_typedef()) {
+ indent(out) << ", \"" << ((t_typedef*)type)->get_symbolic() << "\"";
+ }
+ }
+ out << ")";
+ indent_down();
+ indent_down();
+}
+
+/**
+ * Generates a thrift service. In C++, this comprises an entirely separate
+ * header and source file. The header file defines the methods and includes
+ * the data types defined in the main header file, and the implementation
+ * file contains implementations of the basic printer and default interfaces.
+ *
+ * @param tservice The service definition
+ */
+void t_javame_generator::generate_service(t_service* tservice) {
+ // Make output file
+ string f_service_name = package_dir_ + "/" + service_name_ + ".java";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports();
+
+ f_service_ << "public class " << service_name_ << " {" << endl << endl;
+ indent_up();
+
+ // Generate the three main parts of the service
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+
+ indent_down();
+ f_service_ << "}" << endl;
+ f_service_.close();
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_javame_generator::generate_primitive_service_interface(t_service* tservice) {
+ f_service_ << indent() << "public interface Iface extends " << service_name_ << "Iface { }"
+ << endl << endl;
+
+ string f_interface_name = package_dir_ + "/" + service_name_ + "Iface.java";
+ ofstream_with_content_based_conditional_update f_iface;
+ f_iface.open(f_interface_name.c_str());
+
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends_iface = " extends " + type_name(tservice->get_extends()) + "Iface";
+ }
+
+ f_iface << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports();
+ generate_java_doc(f_iface, tservice);
+ f_iface << "public interface " << service_name_ << "Iface" << extends_iface << " {" << endl
+ << endl;
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_java_doc(f_iface, *f_iter);
+ f_iface << " public " << function_signature(*f_iter) << ";" << endl << endl;
+ }
+ f_iface << "}" << endl << endl;
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_javame_generator::generate_service_interface(t_service* tservice) {
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " extends " + extends + ".Iface";
+ }
+
+ generate_java_doc(f_service_, tservice);
+ f_service_ << indent() << "public interface Iface" << extends_iface << " {" << endl << endl;
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_java_doc(f_service_, *f_iter);
+ indent(f_service_) << "public " << function_signature(*f_iter) << ";" << endl << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates structs for all the service args and return types
+ *
+ * @param tservice The service
+ */
+void t_javame_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_java_struct_definition(f_service_, ts, false, true);
+ generate_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_javame_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_client = " extends " + extends + ".Client";
+ }
+
+ indent(f_service_) << "public static class Client" << extends_client
+ << " implements TServiceClient, Iface {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public Client(TProtocol prot)" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "this(prot, prot);" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << "public Client(TProtocol iprot, TProtocol oprot)" << endl;
+ scope_up(f_service_);
+ if (extends.empty()) {
+ f_service_ << indent() << "iprot_ = iprot;" << endl << indent() << "oprot_ = oprot;" << endl;
+ } else {
+ f_service_ << indent() << "super(iprot, oprot);" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected TProtocol iprot_;" << endl << indent()
+ << "protected TProtocol oprot_;" << endl << endl << indent()
+ << "protected int seqid_;" << endl << endl;
+
+ indent(f_service_) << "public TProtocol getInputProtocol()" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return this.iprot_;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ indent(f_service_) << "public TProtocol getOutputProtocol()" << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "return this.oprot_;" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ indent(f_service_) << "public " << function_signature(*f_iter) << endl;
+ scope_up(f_service_);
+ indent(f_service_) << "send_" << funname << "(";
+
+ // Get the struct of function call params
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+
+ // Declare the function arguments
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << (*fld_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "recv_" << funname << "();" << endl;
+ }
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ t_function send_function(g_type_void,
+ string("send_") + (*f_iter)->get_name(),
+ (*f_iter)->get_arglist());
+
+ string argsname = (*f_iter)->get_name() + "_args";
+
+ // Open function
+ indent(f_service_) << "public " << function_signature(&send_function) << endl;
+ scope_up(f_service_);
+
+ // Serialize the request
+ f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", "
+ << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL")
+ << ", ++seqid_));" << endl << indent() << argsname << " args = new " << argsname
+ << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args.set" << get_cap_name((*fld_iter)->get_name()) << "("
+ << (*fld_iter)->get_name() << ");" << endl;
+ }
+
+ f_service_ << indent() << "args.write(oprot_);" << endl << indent()
+ << "oprot_.writeMessageEnd();" << endl << indent()
+ << "oprot_.getTransport().flush();" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ string resultname = (*f_iter)->get_name() + "_result";
+
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs,
+ (*f_iter)->get_xceptions());
+ // Open function
+ indent(f_service_) << "public " << function_signature(&recv_function) << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "TMessage msg = iprot_.readMessageBegin();" << endl << indent()
+ << "if (msg.type == TMessageType.EXCEPTION) {" << endl << indent()
+ << " TApplicationException x = TApplicationException.read(iprot_);" << endl
+ << indent() << " iprot_.readMessageEnd();" << endl << indent() << " throw x;"
+ << endl << indent() << "}" << endl << indent() << "if (msg.seqid != seqid_) {"
+ << endl << indent()
+ << " throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID, \""
+ << (*f_iter)->get_name() << " failed: out of sequence response\");" << endl
+ << indent() << "}" << endl << indent() << resultname << " result = new "
+ << resultname << "();" << endl << indent() << "result.read(iprot_);" << endl
+ << indent() << "iprot_.readMessageEnd();" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl
+ << indent() << " return result.success;" << endl << indent() << "}" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl
+ << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl
+ << indent() << "}" << endl;
+ }
+
+ // If you get here it's an exception, unless a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "return;" << endl;
+ } else {
+ f_service_ << indent()
+ << "throw new TApplicationException(TApplicationException.MISSING_RESULT, \""
+ << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
+ }
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+ }
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_javame_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Extends stuff
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_processor = " extends " + extends + ".Processor";
+ }
+
+ // Generate the header portion
+ indent(f_service_) << "public static class Processor" << extends_processor
+ << " implements TProcessor {" << endl;
+ indent_up();
+
+ indent(f_service_) << "public Processor(Iface iface)" << endl;
+ scope_up(f_service_);
+ if (!extends.empty()) {
+ f_service_ << indent() << "super(iface);" << endl;
+ }
+ f_service_ << indent() << "iface_ = iface;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "processMap_.put(\"" << (*f_iter)->get_name() << "\", new "
+ << (*f_iter)->get_name() << "());" << endl;
+ }
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ if (extends.empty()) {
+ f_service_
+ << indent() << "protected static interface ProcessFunction {" << endl << indent()
+ << " public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException;"
+ << endl << indent() << "}" << endl << endl;
+ }
+
+ f_service_ << indent() << "private Iface iface_;" << endl;
+
+ if (extends.empty()) {
+ f_service_ << indent() << "protected final Hashtable processMap_ = new Hashtable();" << endl;
+ }
+
+ f_service_ << endl;
+
+ // Generate the server implementation
+ indent(f_service_) << "public boolean process(TProtocol iprot, TProtocol oprot) throws TException"
+ << endl;
+ scope_up(f_service_);
+
+ f_service_ << indent() << "TMessage msg = iprot.readMessageBegin();" << endl;
+
+ // TODO(mcslee): validate message, was the seqid etc. legit?
+
+ f_service_
+ << indent() << "ProcessFunction fn = (ProcessFunction)processMap_.get(msg.name);" << endl
+ << indent() << "if (fn == null) {" << endl << indent()
+ << " TProtocolUtil.skip(iprot, TType.STRUCT);" << endl << indent()
+ << " iprot.readMessageEnd();" << endl << indent()
+ << " TApplicationException x = new "
+ "TApplicationException(TApplicationException.UNKNOWN_METHOD, \"Invalid method name: "
+ "'\"+msg.name+\"'\");" << endl << indent()
+ << " oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));"
+ << endl << indent() << " x.write(oprot);" << endl << indent() << " oprot.writeMessageEnd();"
+ << endl << indent() << " oprot.getTransport().flush();" << endl << indent()
+ << " return true;" << endl << indent() << "}" << endl << indent()
+ << "fn.process(msg.seqid, iprot, oprot);" << endl;
+
+ f_service_ << indent() << "return true;" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_javame_generator::generate_function_helpers(t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_java_struct_definition(f_service_, &result, false, true, true);
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_javame_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open class
+ indent(f_service_) << "private class " << tfunction->get_name() << " implements ProcessFunction {"
+ << endl;
+ indent_up();
+
+ // Open function
+ indent(f_service_)
+ << "public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException"
+ << endl;
+ scope_up(f_service_);
+
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+
+ f_service_ << indent() << argsname << " args = new " << argsname << "();" << endl << indent()
+ << "try {" << endl;
+ indent_up();
+ f_service_ << indent() << "args.read(iprot);" << endl;
+ indent_down();
+ f_service_ << indent() << "} catch (TProtocolException e) {" << endl;
+ indent_up();
+ f_service_ << indent() << "iprot.readMessageEnd();" << endl << indent()
+ << "TApplicationException x = new "
+ "TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage());"
+ << endl << indent() << "oprot.writeMessageBegin(new TMessage(\""
+ << tfunction->get_name() << "\", TMessageType.EXCEPTION, seqid));" << endl << indent()
+ << "x.write(oprot);" << endl << indent() << "oprot.writeMessageEnd();" << endl
+ << indent() << "oprot.getTransport().flush();" << endl << indent() << "return;"
+ << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ f_service_ << indent() << "iprot.readMessageEnd();" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl;
+ }
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "try {" << endl;
+ indent_up();
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.success = ";
+ }
+ f_service_ << "iface_." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ // Set isset on success field
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()
+ && !type_can_be_null(tfunction->get_returntype())) {
+ f_service_ << indent() << "result.set" << get_cap_name("success") << get_cap_name("isSet")
+ << "(true);" << endl;
+ }
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ indent_down();
+ f_service_ << indent() << "}";
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << " catch (" << type_name((*x_iter)->get_type(), false, false) << " "
+ << (*x_iter)->get_name() << ") {" << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ f_service_ << indent() << "result." << (*x_iter)->get_name() << " = "
+ << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ f_service_ << indent() << "}";
+ } else {
+ f_service_ << "}";
+ }
+ }
+ f_service_ << " catch (Throwable th) {" << endl;
+ indent_up();
+ f_service_ << indent() << "TApplicationException x = new "
+ "TApplicationException(TApplicationException.INTERNAL_ERROR, "
+ "\"Internal error processing " << tfunction->get_name() << "\");"
+ << endl << indent() << "oprot.writeMessageBegin(new TMessage(\""
+ << tfunction->get_name() << "\", TMessageType.EXCEPTION, seqid));" << endl
+ << indent() << "x.write(oprot);" << endl << indent() << "oprot.writeMessageEnd();"
+ << endl << indent() << "oprot.getTransport().flush();" << endl << indent()
+ << "return;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "return;" << endl;
+ scope_down(f_service_);
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+ return;
+ }
+
+ f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name()
+ << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);"
+ << endl << indent() << "oprot.writeMessageEnd();" << endl << indent()
+ << "oprot.getTransport().flush();" << endl;
+
+ // Close function
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Close class
+ indent_down();
+ f_service_ << indent() << "}" << endl << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ *
+ * @param tfield The field
+ * @param prefix The variable name or container for this field
+ */
+void t_javame_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type()) {
+ indent(out) << name << " = iprot.";
+
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (!type->is_binary()) {
+ out << "readString();";
+ } else {
+ out << "readBinary();";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool();";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte();";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16();";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32();";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64();";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble();";
+ break;
+ default:
+ throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
+ }
+ out << endl;
+ } else if (type->is_enum()) {
+ indent(out) << name << " = "
+ << type_name(tfield->get_type(), true, false) + ".findByValue(iprot.readI32());"
+ << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, invokes read()
+ */
+void t_javame_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+ out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent()
+ << prefix << ".read(iprot);" << endl;
+}
+
+/**
+ * Deserializes a container by reading its size and then iterating
+ */
+void t_javame_generator::generate_deserialize_container(ostream& out,
+ t_type* ttype,
+ string prefix) {
+ scope_up(out);
+
+ string obj;
+
+ if (ttype->is_map()) {
+ obj = tmp("_map");
+ } else if (ttype->is_set()) {
+ obj = tmp("_set");
+ } else if (ttype->is_list()) {
+ obj = tmp("_list");
+ }
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "TMap " << obj << " = iprot.readMapBegin();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "TSet " << obj << " = iprot.readSetBegin();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "TList " << obj << " = iprot.readListBegin();" << endl;
+ }
+
+ indent(out) << prefix << " = new " << type_name(ttype, false, true)
+ // size the collection correctly
+ << "(" << (ttype->is_list() ? "" : "2*") << obj << ".size"
+ << ");" << endl;
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size"
+ << "; "
+ << "++" << i << ")" << endl;
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot.readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot.readListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_javame_generator::generate_deserialize_map_element(ostream& out,
+ t_map* tmap,
+ string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey) << endl;
+ indent(out) << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << ".put(" << box_type(tmap->get_key_type(), key) << ", "
+ << box_type(tmap->get_val_type(), val) << ");" << endl;
+}
+
+/**
+ * Deserializes a set element
+ */
+void t_javame_generator::generate_deserialize_set_element(ostream& out,
+ t_set* tset,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".put(" << box_type(tset->get_elem_type(), elem) << ", "
+ << box_type(tset->get_elem_type(), elem) << ");" << endl;
+}
+
+/**
+ * Deserializes a list element
+ */
+void t_javame_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".addElement(" << box_type(tlist->get_elem_type(), elem) << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_javame_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name());
+ } else if (type->is_enum()) {
+ indent(out) << "oprot.writeI32(" << prefix + tfield->get_name() << ".getValue());" << endl;
+ } else if (type->is_base_type()) {
+ string name = prefix + tfield->get_name();
+ indent(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ");";
+ } else {
+ out << "writeString(" << name << ");";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ");";
+ break;
+ default:
+ throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ");";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type_name(type).c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_javame_generator::generate_serialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+ (void)tstruct;
+ out << indent() << prefix << ".write(oprot);" << endl;
+}
+
+/**
+ * Serializes a container by writing its size then the elements.
+ *
+ * @param ttype The type of container
+ * @param prefix String prefix for fields
+ */
+void t_javame_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix
+ << ".size()));" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", " << prefix << ".size()));" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListBegin(new TList("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".size()));"
+ << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map()) {
+ string enumer = iter + "_enum";
+ string key_type = type_name(((t_map*)ttype)->get_key_type(), true, false);
+ indent(out) << "for (Enumeration " << enumer << " = " << prefix << ".keys(); " << enumer
+ << ".hasMoreElements(); ) ";
+ scope_up(out);
+ indent(out) << key_type << " " << iter << " = (" << key_type << ")" << enumer
+ << ".nextElement();" << endl;
+ } else if (ttype->is_set()) {
+ string enumer = iter + "_enum";
+ string ele_type = type_name(((t_list*)ttype)->get_elem_type(), true);
+ indent(out) << "for (Enumeration " << enumer << " = " << prefix << ".keys(); " << enumer
+ << ".hasMoreElements(); ) ";
+ scope_up(out);
+ indent(out) << ele_type << " " << iter << " = (" << ele_type << ")" << enumer
+ << ".nextElement();" << endl;
+ } else if (ttype->is_list()) {
+ string enumer = iter + "_enum";
+ indent(out) << "for (Enumeration " << enumer << " = " << prefix << ".elements(); " << enumer
+ << ".hasMoreElements(); ) ";
+ scope_up(out);
+ string ele_type = type_name(((t_list*)ttype)->get_elem_type(), true);
+ indent(out) << ele_type << " " << iter << " = (" << ele_type << ")" << enumer
+ << ".nextElement();" << endl;
+ }
+
+ if (ttype->is_map()) {
+ generate_serialize_map_element(out, (t_map*)ttype, iter, prefix);
+ } else if (ttype->is_set()) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else if (ttype->is_list()) {
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ }
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ */
+void t_javame_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string iter,
+ string map) {
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "");
+ string val_type = type_name(tmap->get_val_type(), true, false);
+ t_field vfield(tmap->get_val_type(), "((" + val_type + ")" + map + ".get(" + iter + "))");
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_javame_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_javame_generator::generate_serialize_list_element(ostream& out,
+ t_list* tlist,
+ string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Returns a Java type name
+ *
+ * @param ttype The type
+ * @param container Is the type going inside a container?
+ * @return Java type name, i.e. Vector
+ */
+string t_javame_generator::type_name(t_type* ttype,
+ bool in_container,
+ bool in_init,
+ bool skip_generic) {
+ (void)in_init;
+ (void)skip_generic;
+ // In Java typedefs are just resolved to their real type
+ ttype = get_true_type(ttype);
+ string prefix;
+
+ if (ttype->is_base_type()) {
+ return base_type_name((t_base_type*)ttype, in_container);
+ } else if (ttype->is_map()) {
+ return "Hashtable";
+ } else if (ttype->is_set()) {
+ return "Hashtable";
+ } else if (ttype->is_list()) {
+ return "Vector";
+ }
+
+ // Check for namespacing
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_) {
+ string package = program->get_namespace("java");
+ if (!package.empty()) {
+ return package + "." + ttype->get_name();
+ }
+ }
+
+ return ttype->get_name();
+}
+
+/**
+ * Returns the C++ type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ * @param container Is it going in a Java container?
+ */
+string t_javame_generator::base_type_name(t_base_type* type, bool in_container) {
+ t_base_type::t_base tbase = type->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ if (!type->is_binary()) {
+ return "String";
+ } else {
+ return "byte[]";
+ }
+ case t_base_type::TYPE_BOOL:
+ return (in_container ? "Boolean" : "boolean");
+ case t_base_type::TYPE_I8:
+ return (in_container ? "Byte" : "byte");
+ case t_base_type::TYPE_I16:
+ return (in_container ? "Short" : "short");
+ case t_base_type::TYPE_I32:
+ return (in_container ? "Integer" : "int");
+ case t_base_type::TYPE_I64:
+ return (in_container ? "Long" : "long");
+ case t_base_type::TYPE_DOUBLE:
+ return (in_container ? "Double" : "double");
+ default:
+ throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_javame_generator::declare_field(t_field* tfield, bool init) {
+ // TODO(mcslee): do we ever need to initialize the field?
+ string result = type_name(tfield->get_type()) + " " + tfield->get_name();
+ if (init) {
+ t_type* ttype = get_true_type(tfield->get_type());
+ if (ttype->is_base_type() && tfield->get_value() != NULL) {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
+ } else if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+
+ } else if (ttype->is_enum()) {
+ result += " = 0";
+ } else if (ttype->is_container()) {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ } else {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ ;
+ }
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_javame_generator::function_signature(t_function* tfunction, string prefix) {
+ t_type* ttype = tfunction->get_returntype();
+ std::string result = type_name(ttype) + " " + prefix + tfunction->get_name() + "("
+ + argument_list(tfunction->get_arglist()) + ") throws ";
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ result += type_name((*x_iter)->get_type(), false, false) + ", ";
+ }
+ result += "TException";
+ return result;
+}
+
+/**
+ * Renders a comma separated field list, with type names
+ */
+string t_javame_generator::argument_list(t_struct* tstruct, bool include_types) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ if (include_types) {
+ result += type_name((*f_iter)->get_type()) + " ";
+ }
+ result += (*f_iter)->get_name();
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_javame_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Applies the correct style to a string based on the value of nocamel_style_
+ */
+std::string t_javame_generator::get_cap_name(std::string name) {
+ name[0] = toupper(name[0]);
+ return name;
+}
+
+string t_javame_generator::constant_name(string name) {
+ string constant_name;
+
+ bool is_first = true;
+ bool was_previous_char_upper = false;
+ for (char character : name) {
+ bool is_upper = isupper(character);
+
+ if (is_upper && !is_first && !was_previous_char_upper) {
+ constant_name += '_';
+ }
+ constant_name += toupper(character);
+
+ is_first = false;
+ was_previous_char_upper = is_upper;
+ }
+
+ return constant_name;
+}
+
+void t_javame_generator::generate_java_docstring_comment(ostream& out, string contents) {
+ generate_docstring_comment(out, "/**\n", " * ", contents, " */\n");
+}
+
+void t_javame_generator::generate_java_doc(ostream& out, t_field* field) {
+ if (field->get_type()->is_enum()) {
+ string combined_message = field->get_doc() + "\n@see " + get_enum_class_name(field->get_type());
+ generate_java_docstring_comment(out, combined_message);
+ } else {
+ generate_java_doc(out, (t_doc*)field);
+ }
+}
+
+/**
+ * Emits a JavaDoc comment if the provided object has a doc in Thrift
+ */
+void t_javame_generator::generate_java_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_java_docstring_comment(out, tdoc->get_doc());
+ }
+}
+
+/**
+ * Emits a JavaDoc comment if the provided function object has a doc in Thrift
+ */
+void t_javame_generator::generate_java_doc(ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc()) {
+ stringstream ss;
+ ss << tfunction->get_doc();
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ss << "\n@param " << p->get_name();
+ if (p->has_doc()) {
+ ss << " " << p->get_doc();
+ }
+ }
+ generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n");
+ }
+}
+
+void t_javame_generator::generate_deep_copy_container(ostream& out,
+ std::string source_name_p1,
+ std::string source_name_p2,
+ std::string result_name,
+ t_type* type) {
+
+ t_container* container = (t_container*)type;
+ std::string source_name;
+ if (source_name_p2 == "")
+ source_name = source_name_p1;
+ else
+ source_name = source_name_p1 + "." + source_name_p2;
+
+ indent(out) << type_name(type, true, false) << " " << result_name << " = new "
+ << type_name(container, false, true) << "();" << endl;
+
+ std::string iterator_element_name = source_name_p1 + "_element";
+ std::string enumeration_name = source_name_p1 + "_enum";
+ std::string result_element_name = result_name + "_copy";
+
+ if (container->is_map()) {
+ t_type* key_type = ((t_map*)container)->get_key_type();
+ t_type* val_type = ((t_map*)container)->get_val_type();
+
+ indent(out) << "for (Enumeration " << enumeration_name << " = " << source_name << ".keys(); "
+ << enumeration_name << ".hasMoreElements(); ) {" << endl;
+ indent_up();
+
+ out << endl;
+
+ indent(out) << type_name(key_type, true, false) << " " << iterator_element_name << "_key = ("
+ << type_name(key_type, true, false) << ")" << enumeration_name << ".nextElement();"
+ << endl;
+ indent(out) << type_name(val_type, true, false) << " " << iterator_element_name << "_value = ("
+ << type_name(val_type, true, false) << ")" << source_name << ".get("
+ << iterator_element_name << "_key);" << endl;
+
+ out << endl;
+
+ if (key_type->is_container()) {
+ generate_deep_copy_container(out,
+ iterator_element_name + "_key",
+ "",
+ result_element_name + "_key",
+ key_type);
+ } else {
+ indent(out) << type_name(key_type, true, false) << " " << result_element_name << "_key = ";
+ generate_deep_copy_non_container(out,
+ iterator_element_name + "_key",
+ result_element_name + "_key",
+ key_type);
+ out << ";" << endl;
+ }
+
+ out << endl;
+
+ if (val_type->is_container()) {
+ generate_deep_copy_container(out,
+ iterator_element_name + "_value",
+ "",
+ result_element_name + "_value",
+ val_type);
+ } else {
+ indent(out) << type_name(val_type, true, false) << " " << result_element_name << "_value = ";
+ generate_deep_copy_non_container(out,
+ iterator_element_name + "_value",
+ result_element_name + "_value",
+ val_type);
+ out << ";" << endl;
+ }
+
+ out << endl;
+
+ indent(out) << result_name << ".put(" << result_element_name << "_key, " << result_element_name
+ << "_value);" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+
+ } else {
+ t_type* elem_type;
+
+ if (container->is_set()) {
+ elem_type = ((t_set*)container)->get_elem_type();
+ } else {
+ elem_type = ((t_list*)container)->get_elem_type();
+ }
+
+ indent(out) << "for (Enumeration " << enumeration_name << " = " << source_name
+ << ".elements(); " << enumeration_name << ".hasMoreElements(); ) {" << endl;
+ indent_up();
+ indent(out) << type_name(elem_type, true, false) << " " << iterator_element_name << " = ("
+ << type_name(elem_type, true, false) << ")" << enumeration_name << ".nextElement();"
+ << endl;
+ if (elem_type->is_container()) {
+ // recursive deep copy
+ generate_deep_copy_container(out, iterator_element_name, "", result_element_name, elem_type);
+ if (elem_type->is_list()) {
+ indent(out) << result_name << ".addElement(" << result_element_name << ");" << endl;
+ } else {
+ indent(out) << result_name << ".put(" << result_element_name << ", " << result_element_name
+ << ");" << endl;
+ }
+ } else {
+ // iterative copy
+ if (elem_type->is_binary()) {
+ indent(out) << type_name(elem_type, true, false) << " temp_binary_element = ";
+ generate_deep_copy_non_container(out,
+ iterator_element_name,
+ "temp_binary_element",
+ elem_type);
+ out << ";" << endl;
+ if (elem_type->is_list()) {
+ indent(out) << result_name << ".addElement(temp_binary_element);" << endl;
+ } else {
+ indent(out) << result_name << ".put(temp_binary_element, temp_binary_element);" << endl;
+ }
+ } else {
+ indent(out) << result_name << ".addElement(";
+ generate_deep_copy_non_container(out, iterator_element_name, result_name, elem_type);
+ out << ");" << endl;
+ }
+ }
+
+ indent_down();
+
+ indent(out) << "}" << endl;
+ }
+}
+
+void t_javame_generator::generate_deep_copy_non_container(ostream& out,
+ std::string source_name,
+ std::string dest_name,
+ t_type* type) {
+ if (type->is_base_type() || type->is_enum() || type->is_typedef()) {
+ // binary fields need to be copied with System.arraycopy
+ if (type->is_binary()) {
+ out << "new byte[" << source_name << ".length];" << endl;
+ indent(out) << "System.arraycopy(" << source_name << ", 0, " << dest_name << ", 0, "
+ << source_name << ".length)";
+ }
+ // everything else can be copied directly
+ else
+ out << source_name;
+ } else {
+ out << "new " << type_name(type, true, true) << "(" << source_name << ")";
+ }
+}
+
+std::string t_javame_generator::generate_isset_check(t_field* field) {
+ return generate_isset_check(field->get_name());
+}
+
+std::string t_javame_generator::isset_field_id(t_field* field) {
+ return "__" + upcase_string(field->get_name() + "_isset_id");
+}
+
+std::string t_javame_generator::generate_isset_check(std::string field_name) {
+ return "is" + get_cap_name("set") + get_cap_name(field_name) + "()";
+}
+
+void t_javame_generator::generate_isset_set(ostream& out, t_field* field) {
+ if (!type_can_be_null(field->get_type())) {
+ indent(out) << "set" << get_cap_name(field->get_name()) << get_cap_name("isSet") << "(true);"
+ << endl;
+ }
+}
+
+std::string t_javame_generator::get_enum_class_name(t_type* type) {
+ string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL && program != program_) {
+ package = program->get_namespace("java") + ".";
+ }
+ return package + type->get_name();
+}
+
+void t_javame_generator::generate_struct_desc(ostream& out, t_struct* tstruct) {
+ indent(out) << "private static final TStruct STRUCT_DESC = new TStruct(\"" << tstruct->get_name()
+ << "\");" << endl;
+}
+
+void t_javame_generator::generate_field_descs(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ indent(out) << "private static final TField " << constant_name((*m_iter)->get_name())
+ << "_FIELD_DESC = new TField(\"" << (*m_iter)->get_name() << "\", "
+ << type_to_enum((*m_iter)->get_type()) << ", "
+ << "(short)" << (*m_iter)->get_key() << ");" << endl;
+ }
+}
+
+bool t_javame_generator::has_bit_vector(t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!type_can_be_null(get_true_type((*m_iter)->get_type()))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void t_javame_generator::generate_java_struct_clear(std::ostream& out, t_struct* tstruct) {
+ indent(out) << "public void clear() {" << endl;
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL) {
+ print_const_value(out,
+ "this." + (*m_iter)->get_name(),
+ t,
+ (*m_iter)->get_value(),
+ true,
+ true);
+ } else {
+ if (type_can_be_null(t)) {
+ indent(out) << "this." << (*m_iter)->get_name() << " = null;" << endl;
+ } else {
+ // must be a base type
+ // means it also needs to be explicitly unset
+ indent(out) << "set" << get_cap_name((*m_iter)->get_name()) << get_cap_name("isSet")
+ << "(false);" << endl;
+ switch (((t_base_type*)t)->get_base()) {
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ indent(out) << "this." << (*m_iter)->get_name() << " = 0;" << endl;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ indent(out) << "this." << (*m_iter)->get_name() << " = 0.0;" << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ indent(out) << "this." << (*m_iter)->get_name() << " = false;" << endl;
+ break;
+ default: // prevent gcc compiler warning
+ break;
+ }
+ }
+ }
+ }
+ indent_down();
+
+ indent(out) << "}" << endl << endl;
+}
+
+THRIFT_REGISTER_GENERATOR(javame, "Java ME", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc
new file mode 100644
index 000000000..dfc398695
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc
@@ -0,0 +1,2948 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <map>
+#include <string>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <vector>
+#include <list>
+#include <cassert>
+#include <unordered_map>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::unordered_map;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+static const string episode_file_name = "thrift.js.episode";
+// largest consecutive integer representable by a double (2 ^ 53 - 1)
+static const int64_t max_safe_integer = 0x1fffffffffffff;
+// smallest consecutive number representable by a double (-2 ^ 53 + 1)
+static const int64_t min_safe_integer = -max_safe_integer;
+
+#include "thrift/generate/t_oop_generator.h"
+
+
+/**
+ * JS code generator.
+ */
+class t_js_generator : public t_oop_generator {
+public:
+ t_js_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ gen_node_ = false;
+ gen_jquery_ = false;
+ gen_ts_ = false;
+ gen_es6_ = false;
+ gen_episode_file_ = false;
+
+ bool with_ns_ = false;
+
+ for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("node") == 0) {
+ gen_node_ = true;
+ } else if( iter->first.compare("jquery") == 0) {
+ gen_jquery_ = true;
+ } else if( iter->first.compare("ts") == 0) {
+ gen_ts_ = true;
+ } else if( iter->first.compare("with_ns") == 0) {
+ with_ns_ = true;
+ } else if( iter->first.compare("es6") == 0) {
+ gen_es6_ = true;
+ } else if( iter->first.compare("imports") == 0) {
+ parse_imports(program, iter->second);
+ } else if (iter->first.compare("thrift_package_output_directory") == 0) {
+ parse_thrift_package_output_directory(iter->second);
+ } else {
+ throw std::invalid_argument("unknown option js:" + iter->first);
+ }
+ }
+
+ if (gen_es6_ && gen_jquery_) {
+ throw std::invalid_argument("invalid switch: [-gen js:es6,jquery] options not compatible");
+ }
+
+ if (gen_node_ && gen_jquery_) {
+ throw std::invalid_argument("invalid switch: [-gen js:node,jquery] options not compatible, try: [-gen js:node -gen "
+ "js:jquery]");
+ }
+
+ if (!gen_node_ && with_ns_) {
+ throw std::invalid_argument("invalid switch: [-gen js:with_ns] is only valid when using node.js");
+ }
+
+ // Depending on the processing flags, we will update these to be ES6 compatible
+ js_const_type_ = "var ";
+ js_let_type_ = "var ";
+ js_var_type_ = "var ";
+ if (gen_es6_) {
+ js_const_type_ = "const ";
+ js_let_type_ = "let ";
+ }
+
+ if (gen_node_) {
+ out_dir_base_ = "gen-nodejs";
+ no_ns_ = !with_ns_;
+ } else {
+ out_dir_base_ = "gen-js";
+ no_ns_ = false;
+ }
+
+ escape_['\''] = "\\'";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_recv_throw(std::string var);
+ std::string render_recv_return(std::string var);
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ /**
+ * Structs!
+ */
+ void generate_js_struct(t_struct* tstruct, bool is_exception);
+ void generate_js_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool is_exported = true);
+ void generate_js_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_js_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_js_function_helpers(t_function* tfunction);
+
+ /**
+ * Service-level generation functions
+ */
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_rest(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_processor(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool inclass = false);
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string js_includes();
+ std::string ts_includes();
+ std::string ts_service_includes();
+ std::string render_includes();
+ std::string render_ts_includes();
+ std::string get_import_path(t_program* program);
+ std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
+ std::string function_signature(t_function* tfunction,
+ std::string prefix = "",
+ bool include_callback = false);
+ std::string argument_list(t_struct* tstruct, bool include_callback = false);
+ std::string type_to_enum(t_type* ttype);
+ std::string make_valid_nodeJs_identifier(std::string const& name);
+
+ /**
+ * Helper parser functions
+ */
+
+ void parse_imports(t_program* program, const std::string& imports_string);
+ void parse_thrift_package_output_directory(const std::string& thrift_package_output_directory);
+
+ std::string autogen_comment() override {
+ return std::string("//\n") + "// Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + "//\n" + "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
+ + "//\n";
+ }
+
+ t_type* get_contained_type(t_type* t);
+
+ std::vector<std::string> js_namespace_pieces(t_program* p) {
+ std::string ns = p->get_namespace("js");
+
+ std::string::size_type loc;
+ std::vector<std::string> pieces;
+
+ if (no_ns_) {
+ return pieces;
+ }
+
+ if (ns.size() > 0) {
+ while ((loc = ns.find(".")) != std::string::npos) {
+ pieces.push_back(ns.substr(0, loc));
+ ns = ns.substr(loc + 1);
+ }
+ }
+
+ if (ns.size() > 0) {
+ pieces.push_back(ns);
+ }
+
+ return pieces;
+ }
+
+ std::string js_type_namespace(t_program* p) {
+ if (gen_node_) {
+ if (p != NULL && p != program_) {
+ return make_valid_nodeJs_identifier(p->get_name()) + "_ttypes.";
+ }
+ return "ttypes.";
+ }
+ return js_namespace(p);
+ }
+
+ std::string js_export_namespace(t_program* p) {
+ if (gen_node_) {
+ return "exports.";
+ }
+ return js_namespace(p);
+ }
+
+ bool has_js_namespace(t_program* p) {
+ if (no_ns_) {
+ return false;
+ }
+ std::string ns = p->get_namespace("js");
+ return (ns.size() > 0);
+ }
+
+ std::string js_namespace(t_program* p) {
+ if (no_ns_) {
+ return "";
+ }
+ std::string ns = p->get_namespace("js");
+ if (ns.size() > 0) {
+ ns += ".";
+ }
+
+ return ns;
+ }
+
+ /**
+ * TypeScript Definition File helper functions
+ */
+
+ string ts_function_signature(t_function* tfunction, bool include_callback);
+ string ts_get_type(t_type* type);
+
+ /**
+ * Special indentation for TypeScript Definitions because of the module.
+ * Returns the normal indentation + " " if a module was defined.
+ * @return string
+ */
+ string ts_indent() { return indent() + (!ts_module_.empty() ? " " : ""); }
+
+ /**
+ * Returns "declare " if no module was defined.
+ * @return string
+ */
+ string ts_declare() { return (ts_module_.empty() ? (gen_node_ ? "declare " : "export declare ") : ""); }
+
+ /**
+ * Returns "?" if the given field is optional or has a default value.
+ * @param t_field The field to check
+ * @return string
+ */
+ string ts_get_req(t_field* field) {return (field->get_req() == t_field::T_OPTIONAL || field->get_value() != NULL ? "?" : ""); }
+
+ /**
+ * Returns the documentation, if the provided documentable object has one.
+ * @param t_doc The object to get the documentation from
+ * @return string The documentation
+ */
+ string ts_print_doc(t_doc* tdoc) {
+ string result = endl;
+
+ if (tdoc->has_doc()) {
+ std::stringstream doc(tdoc->get_doc());
+ string item;
+
+ result += ts_indent() + "/**" + endl;
+ while (std::getline(doc, item)) {
+ result += ts_indent() + " * " + item + endl;
+ }
+ result += ts_indent() + " */" + endl;
+ }
+ return result;
+ }
+
+private:
+ /**
+ * True if we should generate NodeJS-friendly RPC services.
+ */
+ bool gen_node_;
+
+ /**
+ * True if we should generate services that use jQuery ajax (async/sync).
+ */
+ bool gen_jquery_;
+
+ /**
+ * True if we should generate a TypeScript Definition File for each service.
+ */
+ bool gen_ts_;
+
+ /**
+ * True if we should generate ES6 code, i.e. with Promises
+ */
+ bool gen_es6_;
+
+ /**
+ * True if we will generate an episode file.
+ */
+ bool gen_episode_file_;
+
+ /**
+ * The name of the defined module(s), for TypeScript Definition Files.
+ */
+ string ts_module_;
+
+ /**
+ * True if we should not generate namespace objects for node.
+ */
+ bool no_ns_;
+
+ /**
+ * The node modules to use when importing the previously generated files.
+ */
+ vector<string> imports;
+
+ /**
+ * Cache for imported modules.
+ */
+ unordered_map<string, string> module_name_2_import_path;
+
+ /**
+ * Cache for TypeScript includes to generated import name.
+ */
+ unordered_map<t_program*, string> include_2_import_name;
+
+ /**
+ * The prefix to use when generating the episode file.
+ */
+ string thrift_package_output_directory_;
+
+ /**
+ * The variable decorator for "const" variables. Will default to "var" if in an incompatible language.
+ */
+ string js_const_type_;
+
+ /**
+ * The variable decorator for "let" variables. Will default to "var" if in an incompatible language.
+ */
+ string js_let_type_;
+
+ /**
+ * The default variable decorator. Supports all javascript languages, but is not scoped to functions or closures.
+ */
+ string js_var_type_;
+
+ /**
+ * File streams
+ */
+ ofstream_with_content_based_conditional_update f_episode_;
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_service_;
+ ofstream_with_content_based_conditional_update f_types_ts_;
+ ofstream_with_content_based_conditional_update f_service_ts_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_js_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ const auto outdir = get_out_dir();
+
+ // Make output file(s)
+ if (gen_episode_file_) {
+ const auto f_episode_file_path = outdir + episode_file_name;
+ f_episode_.open(f_episode_file_path);
+ }
+
+ const auto f_types_name = outdir + program_->get_name() + "_types.js";
+ f_types_.open(f_types_name.c_str());
+ if (gen_episode_file_) {
+ const auto types_module = program_->get_name() + "_types";
+ f_episode_ << types_module << ":" << thrift_package_output_directory_ << "/" << types_module << endl;
+ }
+
+ if (gen_ts_) {
+ const auto f_types_ts_name = outdir + program_->get_name() + "_types.d.ts";
+ f_types_ts_.open(f_types_ts_name.c_str());
+ }
+
+ // Print header
+ f_types_ << autogen_comment();
+
+ if ((gen_node_ || gen_es6_) && no_ns_) {
+ f_types_ << "\"use strict\";" << endl << endl;
+ }
+
+ f_types_ << js_includes() << endl << render_includes() << endl;
+
+ if (gen_ts_) {
+ f_types_ts_ << autogen_comment() << ts_includes() << endl << render_ts_includes() << endl;
+ }
+
+ if (gen_node_) {
+ f_types_ << js_const_type_ << "ttypes = module.exports = {};" << endl;
+ }
+
+ string pns;
+
+ // setup the namespace
+ // TODO should the namespace just be in the directory structure for node?
+ vector<string> ns_pieces = js_namespace_pieces(program_);
+ if (ns_pieces.size() > 0) {
+ for (size_t i = 0; i < ns_pieces.size(); ++i) {
+ pns += ((i == 0) ? "" : ".") + ns_pieces[i];
+ f_types_ << "if (typeof " << pns << " === 'undefined') {" << endl;
+ f_types_ << " " << pns << " = {};" << endl;
+ f_types_ << "}" << endl;
+ f_types_ << "" << "if (typeof module !== 'undefined' && module.exports) {" << endl;
+ f_types_ << " module.exports." << pns << " = " << pns << ";" << endl << "}" << endl;
+ }
+ if (gen_ts_) {
+ ts_module_ = pns;
+ f_types_ts_ << "declare module " << ts_module_ << " {";
+ }
+ }
+}
+
+/**
+ * Prints standard js imports
+ */
+string t_js_generator::js_includes() {
+ if (gen_node_) {
+ string result = js_const_type_ + "thrift = require('thrift');\n"
+ + js_const_type_ + "Thrift = thrift.Thrift;\n";
+ if (!gen_es6_) {
+ result += js_const_type_ + "Q = thrift.Q;\n";
+ }
+ result += js_const_type_ + "Int64 = require('node-int64');\n";
+ return result;
+ }
+ string result = "if (typeof Int64 === 'undefined' && typeof require === 'function') {\n " + js_const_type_ + "Int64 = require('node-int64');\n}\n";
+ return result;
+}
+
+/**
+ * Prints standard ts imports
+ */
+string t_js_generator::ts_includes() {
+ if (gen_node_) {
+ return string(
+ "import thrift = require('thrift');\n"
+ "import Thrift = thrift.Thrift;\n"
+ "import Q = thrift.Q;\n"
+ "import Int64 = require('node-int64');");
+ }
+ return string("import Int64 = require('node-int64');");
+}
+
+/**
+ * Prints service ts imports
+ */
+string t_js_generator::ts_service_includes() {
+ if (gen_node_) {
+ return string(
+ "import thrift = require('thrift');\n"
+ "import Thrift = thrift.Thrift;\n"
+ "import Q = thrift.Q;\n"
+ "import Int64 = require('node-int64');");
+ }
+ return string("import Int64 = require('node-int64');");
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_js_generator::render_includes() {
+ string result = "";
+
+ if (gen_node_) {
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ result += js_const_type_ + make_valid_nodeJs_identifier(include->get_name()) + "_ttypes = require('" + get_import_path(include) + "');\n";
+ }
+ if (includes.size() > 0) {
+ result += "\n";
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_js_generator::render_ts_includes() {
+ string result;
+
+ if (!gen_node_) {
+ return result;
+ }
+ const vector<t_program*>& includes = program_->get_includes();
+ for (auto include : includes) {
+ string include_name = make_valid_nodeJs_identifier(include->get_name()) + "_ttypes";
+ include_2_import_name.insert({include, include_name});
+ result += "import " + include_name + " = require('" + get_import_path(include) + "');\n";
+ }
+ if (includes.size() > 0) {
+ result += "\n";
+ }
+
+ return result;
+}
+
+string t_js_generator::get_import_path(t_program* program) {
+ const string import_file_name(program->get_name() + "_types");
+ if (program->get_recursive()) {
+ return "./" + import_file_name;
+ }
+
+ const string import_file_name_with_extension = import_file_name + ".js";
+
+ auto module_name_and_import_path_iterator = module_name_2_import_path.find(import_file_name);
+ if (module_name_and_import_path_iterator != module_name_2_import_path.end()) {
+ return module_name_and_import_path_iterator->second;
+ }
+ return "./" + import_file_name;
+}
+
+/**
+ * Close up (or down) some filez.
+ */
+void t_js_generator::close_generator() {
+ // Close types file(s)
+
+ f_types_.close();
+
+ if (gen_ts_) {
+ if (!ts_module_.empty()) {
+ f_types_ts_ << "}";
+ }
+ f_types_ts_.close();
+ }
+ if (gen_episode_file_){
+ f_episode_.close();
+ }
+}
+
+/**
+ * Generates a typedef. This is not done in JS, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_js_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Generates code for an enumerated type. Since define is expensive to lookup
+ * in JS, we use a global array for this.
+ *
+ * @param tenum The enumeration
+ */
+void t_js_generator::generate_enum(t_enum* tenum) {
+ f_types_ << js_type_namespace(tenum->get_program()) << tenum->get_name() << " = {" << endl;
+
+ if (gen_ts_) {
+ f_types_ts_ << ts_print_doc(tenum) << ts_indent() << ts_declare() << "enum "
+ << tenum->get_name() << " {" << endl;
+ }
+
+ indent_up();
+
+ vector<t_enum_value*> const& constants = tenum->get_constants();
+ vector<t_enum_value*>::const_iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ if (gen_ts_) {
+ f_types_ts_ << ts_indent() << (*c_iter)->get_name() << " = " << value << "," << endl;
+ // add 'value: key' in addition to 'key: value' for TypeScript enums
+ f_types_ << indent() << "'" << value << "' : '" << (*c_iter)->get_name() << "'," << endl;
+ }
+ f_types_ << indent() << "'" << (*c_iter)->get_name() << "' : " << value;
+ if (c_iter != constants.end() - 1) {
+ f_types_ << ",";
+ }
+ f_types_ << endl;
+ }
+
+ indent_down();
+
+ f_types_ << "};" << endl;
+
+ if (gen_ts_) {
+ f_types_ts_ << ts_indent() << "}" << endl;
+ }
+}
+
+/**
+ * Generate a constant value
+ */
+void t_js_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ f_types_ << js_type_namespace(program_) << name << " = ";
+ f_types_ << render_const_value(type, value) << ";" << endl;
+
+ if (gen_ts_) {
+ f_types_ts_ << ts_print_doc(tconst) << ts_indent() << ts_declare() << js_const_type_ << name << ": "
+ << ts_get_type(type) << ";" << endl;
+ }
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_js_generator::render_const_value(t_type* type, t_const_value* value) {
+ std::ostringstream out;
+
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << "'" << get_escaped_string(value) << "'";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ {
+ int64_t const& integer_value = value->get_integer();
+ if (integer_value <= max_safe_integer && integer_value >= min_safe_integer) {
+ out << "new Int64(" << integer_value << ")";
+ } else {
+ out << "new Int64('" << std::hex << integer_value << std::dec << "')";
+ }
+ }
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << emit_double_as_string(value->get_double());
+ }
+ break;
+ default:
+ throw std::runtime_error("compiler error: no const of base type " + t_base_type::t_base_name(tbase));
+ }
+ } else if (type->is_enum()) {
+ out << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << "new " << js_type_namespace(type->get_program()) << type->get_name() << "({";
+ indent_up();
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw std::runtime_error("type error: " + type->get_name() + " has no field " + v_iter->first->get_string());
+ }
+ if (v_iter != val.begin())
+ out << ",";
+ out << endl << indent() << render_const_value(g_type_string, v_iter->first);
+ out << " : ";
+ out << render_const_value(field_type, v_iter->second);
+ }
+ indent_down();
+ out << endl << indent() << "})";
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "{" << endl;
+ indent_up();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ if (v_iter != val.begin())
+ out << "," << endl;
+
+ if (ktype->is_base_type() && ((t_base_type*)get_true_type(ktype))->get_base() == t_base_type::TYPE_I64){
+ out << indent() << "\"" << v_iter->first->get_integer() << "\"";
+ } else {
+ out << indent() << render_const_value(ktype, v_iter->first);
+ }
+
+ out << " : ";
+ out << render_const_value(vtype, v_iter->second);
+ }
+ indent_down();
+ out << endl << indent() << "}";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ out << "[";
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ if (v_iter != val.begin())
+ out << ",";
+ out << render_const_value(etype, *v_iter);
+ }
+ out << "]";
+ }
+ return out.str();
+}
+
+/**
+ * Make a struct
+ */
+void t_js_generator::generate_struct(t_struct* tstruct) {
+ generate_js_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_js_generator::generate_xception(t_struct* txception) {
+ generate_js_struct(txception, true);
+}
+
+/**
+ * Structs can be normal or exceptions.
+ */
+void t_js_generator::generate_js_struct(t_struct* tstruct, bool is_exception) {
+ generate_js_struct_definition(f_types_, tstruct, is_exception);
+}
+
+/**
+ * Return type of contained elements for a container type. For maps
+ * this is type of value (keys are always strings in js)
+ */
+t_type* t_js_generator::get_contained_type(t_type* t) {
+ t_type* etype;
+ if (t->is_list()) {
+ etype = ((t_list*)t)->get_elem_type();
+ } else if (t->is_set()) {
+ etype = ((t_set*)t)->get_elem_type();
+ } else {
+ etype = ((t_map*)t)->get_val_type();
+ }
+ return etype;
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is nothing in JS
+ * where the objects are all just associative arrays (unless of course we
+ * decide to start using objects for them...)
+ *
+ * @param tstruct The struct definition
+ */
+void t_js_generator::generate_js_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_exported) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ if (gen_node_) {
+ string prefix = has_js_namespace(tstruct->get_program()) ? js_namespace(tstruct->get_program()) : js_const_type_;
+ out << prefix << tstruct->get_name() <<
+ (is_exported ? " = module.exports." + tstruct->get_name() : "");
+ if (gen_ts_) {
+ f_types_ts_ << ts_print_doc(tstruct) << ts_indent() << ts_declare() << "class "
+ << tstruct->get_name() << (is_exception ? " extends Thrift.TException" : "")
+ << " {" << endl;
+ }
+ } else {
+ out << js_namespace(tstruct->get_program()) << tstruct->get_name();
+ if (gen_ts_) {
+ f_types_ts_ << ts_print_doc(tstruct) << ts_indent() << ts_declare() << "class "
+ << tstruct->get_name() << (is_exception ? " extends Thrift.TException" : "")
+ << " {" << endl;
+ }
+ }
+
+ if (gen_es6_) {
+ if (gen_node_ && is_exception) {
+ out << " = class extends Thrift.TException {" << endl;
+ } else {
+ out << " = class {" << endl;
+ }
+ indent_up();
+ indent(out) << "constructor(args) {" << endl;
+ } else {
+ out << " = function(args) {" << endl;
+ }
+
+ indent_up();
+
+ // Call super() method on inherited Error class
+ if (gen_node_ && is_exception) {
+ if (gen_es6_) {
+ indent(out) << "super(args);" << endl;
+ } else {
+ indent(out) << "Thrift.TException.call(this, \"" << js_namespace(tstruct->get_program())
+ << tstruct->get_name() << "\");" << endl;
+ }
+ out << indent() << "this.name = \"" << js_namespace(tstruct->get_program())
+ << tstruct->get_name() << "\";" << endl;
+ }
+
+ // members with arguments
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string dval = declare_field(*m_iter, false, true);
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) {
+ dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
+ out << indent() << "this." << (*m_iter)->get_name() << " = " << dval << ";" << endl;
+ } else {
+ out << indent() << dval << ";" << endl;
+ }
+ if (gen_ts_) {
+ if (gen_node_) {
+ f_types_ts_ << ts_indent() << "public " << (*m_iter)->get_name() << ": "
+ << ts_get_type((*m_iter)->get_type()) << ";" << endl;
+ } else {
+ f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": "
+ << ts_get_type((*m_iter)->get_type()) << ";" << endl;
+ }
+ }
+ }
+
+ // Generate constructor from array
+ if (members.size() > 0) {
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) {
+ indent(out) << "this." << (*m_iter)->get_name() << " = "
+ << render_const_value(t, (*m_iter)->get_value()) << ";" << endl;
+ }
+ }
+
+ // Early returns for exceptions
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if (t->is_xception()) {
+ out << indent() << "if (args instanceof " << js_type_namespace(t->get_program())
+ << t->get_name() << ") {" << endl << indent() << indent() << "this."
+ << (*m_iter)->get_name() << " = args;" << endl << indent() << indent() << "return;"
+ << endl << indent() << "}" << endl;
+ }
+ }
+
+ indent(out) << "if (args) {" << endl;
+ indent_up();
+ if (gen_ts_) {
+ f_types_ts_ << endl << ts_indent() << "constructor(args?: { ";
+ }
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ indent(out) << "if (args." << (*m_iter)->get_name() << " !== undefined && args." << (*m_iter)->get_name() << " !== null) {" << endl;
+ indent_up();
+ indent(out) << "this." << (*m_iter)->get_name();
+
+ if (t->is_struct()) {
+ out << (" = new " + js_type_namespace(t->get_program()) + t->get_name() +
+ "(args."+(*m_iter)->get_name() +");");
+ out << endl;
+ } else if (t->is_container()) {
+ t_type* etype = get_contained_type(t);
+ string copyFunc = t->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
+ string type_list = "";
+
+ while (etype->is_container()) {
+ if (type_list.length() > 0) {
+ type_list += ", ";
+ }
+ type_list += etype->is_map() ? "Thrift.copyMap" : "Thrift.copyList";
+ etype = get_contained_type(etype);
+ }
+
+ if (etype->is_struct()) {
+ if (type_list.length() > 0) {
+ type_list += ", ";
+ }
+ type_list += js_type_namespace(etype->get_program()) + etype->get_name();
+ }
+ else {
+ if (type_list.length() > 0) {
+ type_list += ", ";
+ }
+ type_list += "null";
+ }
+
+ out << (" = " + copyFunc + "(args." + (*m_iter)->get_name() +
+ ", [" + type_list + "]);");
+ out << endl;
+ } else {
+ out << " = args." << (*m_iter)->get_name() << ";" << endl;
+ }
+
+ indent_down();
+ if (!(*m_iter)->get_req()) {
+ indent(out) << "} else {" << endl;
+ indent(out)
+ << " throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.UNKNOWN, "
+ "'Required field " << (*m_iter)->get_name() << " is unset!');" << endl;
+ }
+ indent(out) << "}" << endl;
+ if (gen_ts_) {
+ f_types_ts_ << (*m_iter)->get_name() << ts_get_req(*m_iter) << ": "
+ << ts_get_type((*m_iter)->get_type()) << "; ";
+ }
+ }
+ indent_down();
+ out << indent() << "}" << endl;
+ if (gen_ts_) {
+ f_types_ts_ << "});" << endl;
+ }
+ }
+
+ // Done with constructor
+ indent_down();
+ if (gen_es6_) {
+ indent(out) << "}" << endl << endl;
+ } else {
+ indent(out) << "};" << endl;
+ }
+
+ if (gen_ts_) {
+ f_types_ts_ << ts_indent() << "}" << endl;
+ }
+
+ if (!gen_es6_) {
+ if (is_exception) {
+ out << "Thrift.inherits(" << js_namespace(tstruct->get_program()) << tstruct->get_name()
+ << ", Thrift.TException);" << endl;
+ out << js_namespace(tstruct->get_program()) << tstruct->get_name() << ".prototype.name = '"
+ << tstruct->get_name() << "';" << endl;
+ } else {
+ // init prototype manually if we aren't using es6
+ out << js_namespace(tstruct->get_program()) << tstruct->get_name() << ".prototype = {};"
+ << endl;
+ }
+
+ }
+
+ generate_js_struct_reader(out, tstruct);
+ generate_js_struct_writer(out, tstruct);
+
+ // Close out the class definition
+ if (gen_es6_) {
+ indent_down();
+ indent(out) << "};" << endl;
+ }
+}
+
+/**
+ * Generates the read() method for a struct
+ */
+void t_js_generator::generate_js_struct_reader(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (gen_es6_) {
+ indent(out) << "read (input) {" << endl;
+ } else {
+ indent(out) << js_namespace(tstruct->get_program()) << tstruct->get_name()
+ << ".prototype.read = function(input) {" << endl;
+ }
+
+ indent_up();
+
+ indent(out) << "input.readStructBegin();" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (true) {" << endl;
+
+ indent_up();
+
+ indent(out) << js_const_type_ << "ret = input.readFieldBegin();" << endl;
+ indent(out) << js_const_type_ << "ftype = ret.ftype;" << endl;
+ if (!fields.empty()) {
+ indent(out) << js_const_type_ << "fid = ret.fid;" << endl;
+ }
+
+ // Check for field STOP marker and break
+ indent(out) << "if (ftype == Thrift.Type.STOP) {" << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ if (!fields.empty()) {
+ // Switch statement on the field we are reading
+ indent(out) << "switch (fid) {" << endl;
+
+ indent_up();
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+
+ indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+
+ indent_up();
+ generate_deserialize_field(out, *f_iter, "this.");
+ indent_down();
+
+ indent(out) << "} else {" << endl;
+
+ indent(out) << " input.skip(ftype);" << endl;
+
+ out << indent() << "}" << endl << indent() << "break;" << endl;
+ }
+ if (fields.size() == 1) {
+ // pseudo case to make jslint happy
+ indent(out) << "case 0:" << endl;
+ indent(out) << " input.skip(ftype);" << endl;
+ indent(out) << " break;" << endl;
+ }
+ // In the default case we skip the field
+ indent(out) << "default:" << endl;
+ indent(out) << " input.skip(ftype);" << endl;
+
+ scope_down(out);
+ } else {
+ indent(out) << "input.skip(ftype);" << endl;
+ }
+
+ indent(out) << "input.readFieldEnd();" << endl;
+
+ scope_down(out);
+
+ indent(out) << "input.readStructEnd();" << endl;
+
+ indent(out) << "return;" << endl;
+
+ indent_down();
+
+ if (gen_es6_) {
+ indent(out) << "}" << endl << endl;
+ } else {
+ indent(out) << "};" << endl << endl;
+ }
+}
+
+/**
+ * Generates the write() method for a struct
+ */
+void t_js_generator::generate_js_struct_writer(ostream& out, t_struct* tstruct) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (gen_es6_) {
+ indent(out) << "write (output) {" << endl;
+ } else {
+ indent(out) << js_namespace(tstruct->get_program()) << tstruct->get_name()
+ << ".prototype.write = function(output) {" << endl;
+ }
+
+ indent_up();
+
+ indent(out) << "output.writeStructBegin('" << name << "');" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ out << indent() << "if (this." << (*f_iter)->get_name() << " !== null && this."
+ << (*f_iter)->get_name() << " !== undefined) {" << endl;
+ indent_up();
+
+ indent(out) << "output.writeFieldBegin("
+ << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type())
+ << ", " << (*f_iter)->get_key() << ");" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this.");
+
+ indent(out) << "output.writeFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ out << indent() << "output.writeFieldStop();" << endl << indent() << "output.writeStructEnd();"
+ << endl;
+
+ out << indent() << "return;" << endl;
+
+ indent_down();
+ if (gen_es6_) {
+ out << indent() << "}" << endl << endl;
+ } else {
+ out << indent() << "};" << endl << endl;
+ }
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_js_generator::generate_service(t_service* tservice) {
+ string f_service_name = get_out_dir() + service_name_ + ".js";
+ f_service_.open(f_service_name.c_str());
+ if (gen_episode_file_) {
+ f_episode_ << service_name_ << ":" << thrift_package_output_directory_ << "/" << service_name_ << endl;
+ }
+
+ if (gen_ts_) {
+ string f_service_ts_name = get_out_dir() + service_name_ + ".d.ts";
+ f_service_ts_.open(f_service_ts_name.c_str());
+ }
+
+ f_service_ << autogen_comment();
+
+ if ((gen_node_ || gen_es6_) && no_ns_) {
+ f_service_ << "\"use strict\";" << endl << endl;
+ }
+
+ f_service_ << js_includes() << endl << render_includes() << endl;
+
+ if (gen_ts_) {
+ if (tservice->get_extends() != NULL) {
+ f_service_ts_ << "/// <reference path=\"" << tservice->get_extends()->get_name()
+ << ".d.ts\" />" << endl;
+ }
+ f_service_ts_ << autogen_comment() << endl << ts_includes() << endl << render_ts_includes() << endl;
+ if (gen_node_) {
+ f_service_ts_ << "import ttypes = require('./" + program_->get_name() + "_types');" << endl;
+ // Generate type aliases
+ // enum
+ vector<t_enum*> const& enums = program_->get_enums();
+ vector<t_enum*>::const_iterator e_iter;
+ for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) {
+ f_service_ts_ << "import " << (*e_iter)->get_name() << " = ttypes."
+ << js_namespace(program_) << (*e_iter)->get_name() << endl;
+ }
+ // const
+ vector<t_const*> const& consts = program_->get_consts();
+ vector<t_const*>::const_iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ f_service_ts_ << "import " << (*c_iter)->get_name() << " = ttypes."
+ << js_namespace(program_) << (*c_iter)->get_name() << endl;
+ }
+ // exception
+ vector<t_struct*> const& exceptions = program_->get_xceptions();
+ vector<t_struct*>::const_iterator x_iter;
+ for (x_iter = exceptions.begin(); x_iter != exceptions.end(); ++x_iter) {
+ f_service_ts_ << "import " << (*x_iter)->get_name() << " = ttypes."
+ << js_namespace(program_) << (*x_iter)->get_name() << endl;
+ }
+ // structs
+ vector<t_struct*> const& structs = program_->get_structs();
+ vector<t_struct*>::const_iterator s_iter;
+ for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) {
+ f_service_ts_ << "import " << (*s_iter)->get_name() << " = ttypes."
+ << js_namespace(program_) << (*s_iter)->get_name() << endl;
+ }
+ } else {
+ f_service_ts_ << "import { " << program_->get_name() << " } from \"./" << program_->get_name() << "_types\";" << endl << endl;
+ }
+ if (!ts_module_.empty()) {
+ if (gen_node_) {
+ f_service_ts_ << "declare module " << ts_module_ << " {";
+ } else {
+ f_service_ts_ << "declare module \"./" << program_->get_name() << "_types\" {" << endl;
+ indent_up();
+ f_service_ts_ << ts_indent() << "module " << program_->get_name() << " {" << endl;
+ indent_up();
+ }
+ }
+ }
+
+ if (gen_node_) {
+ if (tservice->get_extends() != NULL) {
+ f_service_ << js_const_type_ << tservice->get_extends()->get_name() << " = require('./"
+ << tservice->get_extends()->get_name() << "');" << endl << js_const_type_
+ << tservice->get_extends()->get_name()
+ << "Client = " << tservice->get_extends()->get_name() << ".Client;" << endl
+ << js_const_type_ << tservice->get_extends()->get_name()
+ << "Processor = " << tservice->get_extends()->get_name() << ".Processor;" << endl;
+ }
+
+ f_service_ << js_const_type_ << "ttypes = require('./" + program_->get_name() + "_types');" << endl;
+ }
+
+ generate_service_helpers(tservice);
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+
+ if (gen_node_) {
+ generate_service_processor(tservice);
+ }
+
+ f_service_.close();
+ if (gen_ts_) {
+ if (!ts_module_.empty()) {
+ if (gen_node_) {
+ f_service_ts_ << "}" << endl;
+ } else {
+ indent_down();
+ f_service_ts_ << ts_indent() << "}" << endl;
+ f_service_ts_ << "}" << endl;
+ }
+ }
+ f_service_ts_.close();
+ }
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_js_generator::generate_service_processor(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ if (gen_node_) {
+ string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_;
+ f_service_ << prefix << service_name_ << "Processor = " << "exports.Processor";
+ if (gen_ts_) {
+ f_service_ts_ << endl << "declare class Processor ";
+ if (tservice->get_extends() != NULL) {
+ f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Processor ";
+ }
+ f_service_ts_ << "{" << endl;
+ indent_up();
+ f_service_ts_ << ts_indent() << "private _handler: object;" << endl << endl;
+ f_service_ts_ << ts_indent() << "constructor(handler: object);" << endl;
+ f_service_ts_ << ts_indent() << "process(input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl;
+ indent_down();
+ }
+ } else {
+ f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Processor = "
+ << "exports.Processor";
+ }
+
+ bool is_subclass_service = tservice->get_extends() != NULL;
+
+ // ES6 Constructor
+ if (gen_es6_) {
+ if (is_subclass_service) {
+ f_service_ << " = class extends " << tservice->get_extends()->get_name() << "Processor {" << endl;
+ } else {
+ f_service_ << " = class {" << endl;
+ }
+ indent_up();
+ indent(f_service_) << "constructor(handler) {" << endl;
+ } else {
+ f_service_ << " = function(handler) {" << endl;
+ }
+
+ indent_up();
+ if (gen_es6_ && is_subclass_service) {
+ indent(f_service_) << "super(handler);" << endl;
+ }
+ indent(f_service_) << "this._handler = handler;" << endl;
+ indent_down();
+
+ // Done with constructor
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ }
+
+ // ES5 service inheritance
+ if (!gen_es6_ && is_subclass_service) {
+ indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program())
+ << service_name_ << "Processor, " << tservice->get_extends()->get_name()
+ << "Processor);" << endl;
+ }
+
+ // Generate the server implementation
+ if (gen_es6_) {
+ indent(f_service_) << "process (input, output) {" << endl;
+ } else {
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
+ << "Processor.prototype.process = function(input, output) {" << endl;
+ }
+
+ indent_up();
+
+ indent(f_service_) << js_const_type_ << "r = input.readMessageBegin();" << endl << indent()
+ << "if (this['process_' + r.fname]) {" << endl << indent()
+ << " return this['process_' + r.fname].call(this, r.rseqid, input, output);" << endl
+ << indent() << "} else {" << endl << indent() << " input.skip(Thrift.Type.STRUCT);"
+ << endl << indent() << " input.readMessageEnd();" << endl << indent()
+ << " " << js_const_type_ << "x = new "
+ "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN_METHOD, "
+ "'Unknown function ' + r.fname);" << endl << indent()
+ << " output.writeMessageBegin(r.fname, Thrift.MessageType.EXCEPTION, r.rseqid);"
+ << endl << indent() << " x.write(output);" << endl << indent()
+ << " output.writeMessageEnd();" << endl << indent() << " output.flush();" << endl
+ << indent() << "}" << endl;
+
+ indent_down();
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ }
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ // Close off the processor class definition
+ if (gen_es6_) {
+ indent_down();
+ indent(f_service_) << "};" << endl;
+ }
+ if (gen_node_ && gen_ts_) {
+ f_service_ts_ << "}" << endl;
+ }
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_js_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ if (gen_es6_) {
+ indent(f_service_) << "process_" + tfunction->get_name() + " (seqid, input, output) {" << endl;
+ } else {
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
+ << "Processor.prototype.process_" + tfunction->get_name()
+ + " = function(seqid, input, output) {" << endl;
+ }
+ if (gen_ts_) {
+ indent_up();
+ f_service_ts_ << ts_indent() << "process_" << tfunction->get_name() << "(seqid: number, input: thrift.TProtocol, output: thrift.TProtocol): void;" << endl;
+ indent_down();
+ }
+
+ indent_up();
+
+ string argsname = js_namespace(program_) + service_name_ + "_" + tfunction->get_name() + "_args";
+ string resultname = js_namespace(program_) + service_name_ + "_" + tfunction->get_name()
+ + "_result";
+
+ indent(f_service_) << js_const_type_ << "args = new " << argsname << "();" << endl << indent()
+ << "args.read(input);" << endl << indent() << "input.readMessageEnd();" << endl;
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ indent(f_service_) << "this._handler." << tfunction->get_name() << "(";
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+
+ f_service_ << ");" << endl;
+ indent_down();
+
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ }
+ return;
+ }
+
+ // Promise style invocation
+ indent(f_service_) << "if (this._handler." << tfunction->get_name()
+ << ".length === " << fields.size() << ") {" << endl;
+ indent_up();
+
+ if (gen_es6_) {
+ indent(f_service_) << "Promise.resolve(this._handler." << tfunction->get_name() << ".bind(this._handler)(" << endl;
+ } else {
+ string maybeComma = (fields.size() > 0 ? "," : "");
+ indent(f_service_) << "Q.fcall(this._handler." << tfunction->get_name() << ".bind(this._handler)"
+ << maybeComma << endl;
+ }
+
+ indent_up();
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ string maybeComma = (f_iter != fields.end() - 1 ? "," : "");
+ indent(f_service_) << "args." << (*f_iter)->get_name() << maybeComma << endl;
+ }
+ indent_down();
+
+ if (gen_es6_) {
+ indent(f_service_) << ")).then(result => {" << endl;
+ } else {
+ indent(f_service_) << ").then(function(result) {" << endl;
+ }
+
+ indent_up();
+ f_service_ << indent() << js_const_type_ << "result_obj = new " << resultname << "({success: result});" << endl
+ << indent() << "output.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", Thrift.MessageType.REPLY, seqid);" << endl << indent()
+ << "result_obj.write(output);" << endl << indent() << "output.writeMessageEnd();" << endl
+ << indent() << "output.flush();" << endl;
+ indent_down();
+
+ if (gen_es6_) {
+ indent(f_service_) << "}).catch(err => {" << endl;
+ } else {
+ indent(f_service_) << "}).catch(function (err) {" << endl;
+ }
+ indent_up();
+ indent(f_service_) << js_let_type_ << "result;" << endl;
+
+ bool has_exception = false;
+ t_struct* exceptions = tfunction->get_xceptions();
+ if (exceptions) {
+ const vector<t_field*>& members = exceptions->get_members();
+ for (auto member : members) {
+ t_type* t = get_true_type(member->get_type());
+ if (t->is_xception()) {
+ if (!has_exception) {
+ has_exception = true;
+ indent(f_service_) << "if (err instanceof " << js_type_namespace(t->get_program())
+ << t->get_name();
+ } else {
+ f_service_ << " || err instanceof " << js_type_namespace(t->get_program())
+ << t->get_name();
+ }
+ }
+ }
+ }
+
+ if (has_exception) {
+ f_service_ << ") {" << endl;
+ indent_up();
+ f_service_ << indent() << "result = new " << resultname << "(err);" << endl << indent()
+ << "output.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", Thrift.MessageType.REPLY, seqid);" << endl;
+
+ indent_down();
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+ }
+
+ f_service_ << indent() << "result = new "
+ "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN,"
+ " err.message);" << endl << indent() << "output.writeMessageBegin(\""
+ << tfunction->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl;
+
+ if (has_exception) {
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ }
+
+ f_service_ << indent() << "result.write(output);" << endl << indent()
+ << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl;
+ indent_down();
+ indent(f_service_) << "});" << endl;
+ indent_down();
+ // End promise style invocation
+
+ // Callback style invocation
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+ indent(f_service_) << "this._handler." << tfunction->get_name() << "(";
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ f_service_ << "args." << (*f_iter)->get_name() << ", ";
+ }
+
+ if (gen_es6_) {
+ f_service_ << "(err, result) => {" << endl;
+ } else {
+ f_service_ << "function (err, result) {" << endl;
+ }
+ indent_up();
+ indent(f_service_) << js_let_type_ << "result_obj;" << endl;
+
+ indent(f_service_) << "if ((err === null || typeof err === 'undefined')";
+ if (has_exception) {
+ const vector<t_field*>& members = exceptions->get_members();
+ for (auto member : members) {
+ t_type* t = get_true_type(member->get_type());
+ if (t->is_xception()) {
+ f_service_ << " || err instanceof " << js_type_namespace(t->get_program()) << t->get_name();
+ }
+ }
+ }
+ f_service_ << ") {" << endl;
+ indent_up();
+ f_service_ << indent() << "result_obj = new " << resultname
+ << "((err !== null || typeof err === 'undefined') ? err : {success: result});" << endl << indent()
+ << "output.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", Thrift.MessageType.REPLY, seqid);" << endl;
+ indent_down();
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+ f_service_ << indent() << "result_obj = new "
+ "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN,"
+ " err.message);" << endl << indent() << "output.writeMessageBegin(\""
+ << tfunction->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl << indent() << "result_obj.write(output);" << endl << indent()
+ << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl;
+
+ indent_down();
+ indent(f_service_) << "});" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ // End callback style invocation
+
+ indent_down();
+
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ }
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_js_generator::generate_service_helpers(t_service* tservice) {
+ // Do not generate TS definitions for helper functions
+ bool gen_ts_tmp = gen_ts_;
+ gen_ts_ = false;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ f_service_ << "//HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ string name = ts->get_name();
+ ts->set_name(service_name_ + "_" + name);
+ generate_js_struct_definition(f_service_, ts, false, false);
+ generate_js_function_helpers(*f_iter);
+ ts->set_name(name);
+ }
+
+ gen_ts_ = gen_ts_tmp;
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_js_generator::generate_js_function_helpers(t_function* tfunction) {
+ t_struct result(program_, service_name_ + "_" + tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_js_struct_definition(f_service_, &result, false, false);
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_js_generator::generate_service_interface(t_service* tservice) {
+ (void)tservice;
+}
+
+/**
+ * Generates a REST interface
+ */
+void t_js_generator::generate_service_rest(t_service* tservice) {
+ (void)tservice;
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_js_generator::generate_service_client(t_service* tservice) {
+
+ bool is_subclass_service = tservice->get_extends() != NULL;
+
+ if (gen_node_) {
+ string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : js_const_type_;
+ f_service_ << prefix << service_name_ << "Client = " << "exports.Client";
+ if (gen_ts_) {
+ f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class "
+ << "Client ";
+ if (tservice->get_extends() != NULL) {
+ f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client ";
+ }
+ f_service_ts_ << "{" << endl;
+ }
+ } else {
+ f_service_ << js_namespace(tservice->get_program()) << service_name_
+ << "Client";
+ if (gen_ts_) {
+ f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class "
+ << service_name_ << "Client ";
+ if (is_subclass_service) {
+ f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client ";
+ }
+ f_service_ts_ << "{" << endl;
+ }
+ }
+
+ // ES6 Constructor
+ if (gen_es6_) {
+ if (is_subclass_service) {
+ f_service_ << " = class extends " << js_namespace(tservice->get_extends()->get_program())
+ << tservice->get_extends()->get_name() << "Client {" << endl;
+ } else {
+ f_service_ << " = class {" << endl;
+ }
+ indent_up();
+ if (gen_node_) {
+ indent(f_service_) << "constructor(output, pClass) {" << endl;
+ } else {
+ indent(f_service_) << "constructor(input, output) {" << endl;
+ }
+ } else {
+ if (gen_node_) {
+ f_service_ << " = function(output, pClass) {" << endl;
+ } else {
+ f_service_ << " = function(input, output) {" << endl;
+ }
+ }
+
+ indent_up();
+
+ if (gen_node_) {
+ indent(f_service_) << "this.output = output;" << endl;
+ indent(f_service_) << "this.pClass = pClass;" << endl;
+ indent(f_service_) << "this._seqid = 0;" << endl;
+ indent(f_service_) << "this._reqs = {};" << endl;
+ if (gen_ts_) {
+ f_service_ts_ << ts_indent() << "private output: thrift.TTransport;" << endl
+ << ts_indent() << "private pClass: thrift.TProtocol;" << endl
+ << ts_indent() << "private _seqid: number;" << endl
+ << endl
+ << ts_indent() << "constructor(output: thrift.TTransport, pClass: { new(trans: thrift.TTransport): thrift.TProtocol });"
+ << endl;
+ }
+ } else {
+ indent(f_service_) << "this.input = input;" << endl;
+ indent(f_service_) << "this.output = (!output) ? input : output;" << endl;
+ indent(f_service_) << "this.seqid = 0;" << endl;
+ if (gen_ts_) {
+ f_service_ts_ << ts_indent() << "input: Thrift.TJSONProtocol;" << endl << ts_indent()
+ << "output: Thrift.TJSONProtocol;" << endl << ts_indent() << "seqid: number;"
+ << endl << endl << ts_indent()
+ << "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);"
+ << endl;
+ }
+ }
+
+ indent_down();
+
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ if (is_subclass_service) {
+ indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program())
+ << service_name_ << "Client, "
+ << js_namespace(tservice->get_extends()->get_program())
+ << tservice->get_extends()->get_name() << "Client);" << endl;
+ } else {
+ // init prototype
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
+ << "Client.prototype = {};" << endl;
+ }
+ }
+
+ // utils for multiplexed services
+ if (gen_node_) {
+ if (gen_es6_) {
+ indent(f_service_) << "seqid () { return this._seqid; }" << endl;
+ indent(f_service_) << "new_seqid () { return this._seqid += 1; }" << endl;
+ } else {
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
+ << "Client.prototype.seqid = function() { return this._seqid; };" << endl
+ << js_namespace(tservice->get_program()) << service_name_
+ << "Client.prototype.new_seqid = function() { return this._seqid += 1; };"
+ << endl;
+ }
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+ string arglist = argument_list(arg_struct);
+
+ // Open function
+ f_service_ << endl;
+ if (gen_es6_) {
+ indent(f_service_) << funname << " (" << arglist << ") {" << endl;
+ } else {
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype."
+ << function_signature(*f_iter, "", !gen_es6_) << " {" << endl;
+ }
+
+ indent_up();
+
+ if (gen_ts_) {
+ // function definition without callback
+ f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, false) << endl;
+ if (!gen_es6_) {
+ // overload with callback
+ f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl;
+ } else {
+ // overload with callback
+ f_service_ts_ << ts_print_doc(*f_iter) << ts_indent() << ts_function_signature(*f_iter, true) << endl;
+ }
+ }
+
+ if (gen_es6_ && gen_node_) {
+ indent(f_service_) << "this._seqid = this.new_seqid();" << endl;
+ indent(f_service_) << js_const_type_ << "self = this;" << endl << indent()
+ << "return new Promise((resolve, reject) => {" << endl;
+ indent_up();
+ indent(f_service_) << "self._reqs[self.seqid()] = (error, result) => {" << endl;
+ indent_up();
+ indent(f_service_) << "return error ? reject(error) : resolve(result);" << endl;
+ indent_down();
+ indent(f_service_) << "};" << endl;
+ indent(f_service_) << "self.send_" << funname << "(" << arglist << ");" << endl;
+ indent_down();
+ indent(f_service_) << "});" << endl;
+ } else if (gen_node_) { // Node.js output ./gen-nodejs
+ f_service_ << indent() << "this._seqid = this.new_seqid();" << endl << indent()
+ << "if (callback === undefined) {" << endl;
+ indent_up();
+ f_service_ << indent() << js_const_type_ << "_defer = Q.defer();" << endl << indent()
+ << "this._reqs[this.seqid()] = function(error, result) {" << endl;
+ indent_up();
+ indent(f_service_) << "if (error) {" << endl;
+ indent_up();
+ indent(f_service_) << "_defer.reject(error);" << endl;
+ indent_down();
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+ indent(f_service_) << "_defer.resolve(result);" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ indent_down();
+ indent(f_service_) << "};" << endl;
+ f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl
+ << indent() << "return _defer.promise;" << endl;
+ indent_down();
+ indent(f_service_) << "} else {" << endl;
+ indent_up();
+ f_service_ << indent() << "this._reqs[this.seqid()] = callback;" << endl << indent()
+ << "this.send_" << funname << "(" << arglist << ");" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl;
+ } else if (gen_es6_) {
+ f_service_ << indent() << js_const_type_ << "self = this;" << endl << indent()
+ << "return new Promise((resolve, reject) => {" << endl;
+ indent_up();
+ f_service_ << indent() << "self.send_" << funname << "(" << arglist
+ << (arglist.empty() ? "" : ", ") << "(error, result) => {" << endl;
+ indent_up();
+ indent(f_service_) << "return error ? reject(error) : resolve(result);" << endl;
+ indent_down();
+ f_service_ << indent() << "});" << endl;
+ indent_down();
+ f_service_ << indent() << "});" << endl;
+
+ } else if (gen_jquery_) { // jQuery output ./gen-js
+ f_service_ << indent() << "if (callback === undefined) {" << endl;
+ indent_up();
+ f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl;
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "this.recv_" << funname << "();" << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "} else {" << endl;
+ indent_up();
+ f_service_ << indent() << js_const_type_ << "postData = this.send_" << funname << "(" << arglist
+ << (arglist.empty() ? "" : ", ") << "true);" << endl;
+ f_service_ << indent() << "return this.output.getTransport()" << endl;
+ indent_up();
+ f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname
+ << ");" << endl;
+ indent_down();
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ } else { // Standard JavaScript ./gen-js
+ f_service_ << indent() << "this.send_" << funname << "(" << arglist
+ << (arglist.empty() ? "" : ", ") << "callback); " << endl;
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent() << "if (!callback) {" << endl;
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << " return ";
+ }
+ f_service_ << "this.recv_" << funname << "();" << endl;
+ f_service_ << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl << endl;
+ } else {
+ indent(f_service_) << "};" << endl << endl;
+ }
+
+ // Send function
+ if (gen_es6_) {
+ if (gen_node_) {
+ indent(f_service_) << "send_" << funname << " (" << arglist << ") {" << endl;
+ } else {
+ // ES6 js still uses callbacks here. Should refactor this to promise style later..
+ indent(f_service_) << "send_" << funname << " (" << argument_list(arg_struct, true) << ") {" << endl;
+ }
+ } else {
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype.send_"
+ << function_signature(*f_iter, "", !gen_node_) << " {" << endl;
+ }
+
+ indent_up();
+
+ std::string outputVar;
+ if (gen_node_) {
+ f_service_ << indent() << js_const_type_ << "output = new this.pClass(this.output);" << endl;
+ outputVar = "output";
+ } else {
+ outputVar = "this.output";
+ }
+
+ std::string argsname = js_namespace(program_) + service_name_ + "_" + (*f_iter)->get_name()
+ + "_args";
+
+ std::string messageType = (*f_iter)->is_oneway() ? "Thrift.MessageType.ONEWAY"
+ : "Thrift.MessageType.CALL";
+ // Build args
+ if (fields.size() > 0){
+ f_service_ << indent() << js_const_type_ << "params = {" << endl;
+ indent_up();
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ indent(f_service_) << (*fld_iter)->get_name() << ": " << (*fld_iter)->get_name();
+ if (fld_iter != fields.end()-1) {
+ f_service_ << "," << endl;
+ } else {
+ f_service_ << endl;
+ }
+ }
+ indent_down();
+ indent(f_service_) << "};" << endl;
+ indent(f_service_) << js_const_type_ << "args = new " << argsname << "(params);" << endl;
+ } else {
+ indent(f_service_) << js_const_type_ << "args = new " << argsname << "();" << endl;
+ }
+
+
+ // Serialize the request header within try/catch
+ indent(f_service_) << "try {" << endl;
+ indent_up();
+
+ if (gen_node_) {
+ f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name()
+ << "', " << messageType << ", this.seqid());" << endl;
+ } else {
+ f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name()
+ << "', " << messageType << ", this.seqid);" << endl;
+ }
+
+
+ // Write to the stream
+ f_service_ << indent() << "args.write(" << outputVar << ");" << endl << indent() << outputVar
+ << ".writeMessageEnd();" << endl;
+
+ if (gen_node_) {
+ if((*f_iter)->is_oneway()) {
+ f_service_ << indent() << "this.output.flush();" << endl;
+ f_service_ << indent() << js_const_type_ << "callback = this._reqs[this.seqid()] || function() {};" << endl;
+ f_service_ << indent() << "delete this._reqs[this.seqid()];" << endl;
+ f_service_ << indent() << "callback(null);" << endl;
+ } else {
+ f_service_ << indent() << "return this.output.flush();" << endl;
+ }
+ } else {
+ if (gen_jquery_) {
+ f_service_ << indent() << "return this.output.getTransport().flush(callback);" << endl;
+ } else if (gen_es6_) {
+ f_service_ << indent() << js_const_type_ << "self = this;" << endl;
+ if((*f_iter)->is_oneway()) {
+ f_service_ << indent() << "this.output.getTransport().flush(true, null);" << endl;
+ f_service_ << indent() << "callback();" << endl;
+ } else {
+ f_service_ << indent() << "this.output.getTransport().flush(true, () => {" << endl;
+ indent_up();
+ f_service_ << indent() << js_let_type_ << "error = null, result = null;" << endl;
+ f_service_ << indent() << "try {" << endl;
+ f_service_ << indent() << " result = self.recv_" << funname << "();" << endl;
+ f_service_ << indent() << "} catch (e) {" << endl;
+ f_service_ << indent() << " error = e;" << endl;
+ f_service_ << indent() << "}" << endl;
+ f_service_ << indent() << "callback(error, result);" << endl;
+ indent_down();
+ f_service_ << indent() << "});" << endl;
+ }
+ } else {
+ f_service_ << indent() << "if (callback) {" << endl;
+ indent_up();
+ if((*f_iter)->is_oneway()) {
+ f_service_ << indent() << "this.output.getTransport().flush(true, null);" << endl;
+ f_service_ << indent() << "callback();" << endl;
+ } else {
+ f_service_ << indent() << js_const_type_ << "self = this;" << endl;
+ f_service_ << indent() << "this.output.getTransport().flush(true, function() {" << endl;
+ indent_up();
+ f_service_ << indent() << js_let_type_ << "result = null;" << endl;
+ f_service_ << indent() << "try {" << endl;
+ f_service_ << indent() << " result = self.recv_" << funname << "();" << endl;
+ f_service_ << indent() << "} catch (e) {" << endl;
+ f_service_ << indent() << " result = e;" << endl;
+ f_service_ << indent() << "}" << endl;
+ f_service_ << indent() << "callback(result);" << endl;
+ indent_down();
+ f_service_ << indent() << "});" << endl;
+ }
+ indent_down();
+ f_service_ << indent() << "} else {" << endl;
+ f_service_ << indent() << " return this.output.getTransport().flush();" << endl;
+ f_service_ << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+
+ // Reset the transport and delete registered callback if there was a serialization error
+ f_service_ << indent() << "catch (e) {" << endl;
+ indent_up();
+ if (gen_node_) {
+ f_service_ << indent() << "delete this._reqs[this.seqid()];" << endl;
+ f_service_ << indent() << "if (typeof " << outputVar << ".reset === 'function') {" << endl;
+ f_service_ << indent() << " " << outputVar << ".reset();" << endl;
+ f_service_ << indent() << "}" << endl;
+ } else {
+ f_service_ << indent() << "if (typeof " << outputVar << ".getTransport().reset === 'function') {" << endl;
+ f_service_ << indent() << " " << outputVar << ".getTransport().reset();" << endl;
+ f_service_ << indent() << "}" << endl;
+ }
+ f_service_ << indent() << "throw e;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+
+ indent_down();
+
+ // Close send function
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ }
+
+ // Receive function
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultname = js_namespace(tservice->get_program()) + service_name_ + "_"
+ + (*f_iter)->get_name() + "_result";
+
+ f_service_ << endl;
+ // Open receive function
+ if (gen_node_) {
+ if (gen_es6_) {
+ indent(f_service_) << "recv_" << (*f_iter)->get_name() << " (input, mtype, rseqid) {" << endl;
+ } else {
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
+ << "Client.prototype.recv_" << (*f_iter)->get_name()
+ << " = function(input,mtype,rseqid) {" << endl;
+ }
+ } else {
+ if (gen_es6_) {
+ indent(f_service_) << "recv_" << (*f_iter)->get_name() << " () {" << endl;
+ } else {
+ t_struct noargs(program_);
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ indent(f_service_) << js_namespace(tservice->get_program()) << service_name_
+ << "Client.prototype." << function_signature(&recv_function) << " {" << endl;
+ }
+ }
+
+ indent_up();
+
+ std::string inputVar;
+ if (gen_node_) {
+ inputVar = "input";
+ } else {
+ inputVar = "this.input";
+ }
+
+ if (gen_node_) {
+ f_service_ << indent() << js_const_type_ << "callback = this._reqs[rseqid] || function() {};" << endl
+ << indent() << "delete this._reqs[rseqid];" << endl;
+ } else {
+ f_service_ << indent() << js_const_type_ << "ret = this.input.readMessageBegin();" << endl
+ << indent() << js_const_type_ << "mtype = ret.mtype;" << endl;
+ }
+
+ f_service_ << indent() << "if (mtype == Thrift.MessageType.EXCEPTION) {" << endl;
+
+ indent_up();
+ f_service_ << indent() << js_const_type_ << "x = new Thrift.TApplicationException();" << endl
+ << indent() << "x.read(" << inputVar << ");" << endl
+ << indent() << inputVar << ".readMessageEnd();" << endl
+ << indent() << render_recv_throw("x") << endl;
+ scope_down(f_service_);
+
+ f_service_ << indent() << js_const_type_ << "result = new " << resultname << "();" << endl << indent()
+ << "result.read(" << inputVar << ");" << endl;
+
+ f_service_ << indent() << inputVar << ".readMessageEnd();" << endl << endl;
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "if (null !== result." << (*x_iter)->get_name() << ") {" << endl
+ << indent() << " " << render_recv_throw("result." + (*x_iter)->get_name())
+ << endl << indent() << "}" << endl;
+ }
+
+ // Careful, only return result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if (null !== result.success) {" << endl << indent() << " "
+ << render_recv_return("result.success") << endl << indent() << "}" << endl;
+ f_service_ << indent()
+ << render_recv_throw("'" + (*f_iter)->get_name() + " failed: unknown result'")
+ << endl;
+ } else {
+ if (gen_node_) {
+ indent(f_service_) << "callback(null);" << endl;
+ } else {
+ indent(f_service_) << "return;" << endl;
+ }
+ }
+
+ // Close receive function
+ indent_down();
+ if (gen_es6_) {
+ indent(f_service_) << "}" << endl;
+ } else {
+ indent(f_service_) << "};" << endl;
+ }
+ }
+ }
+
+ // Finish class definitions
+ if (gen_ts_) {
+ f_service_ts_ << ts_indent() << "}" << endl;
+ }
+ if (gen_es6_) {
+ indent_down();
+ f_service_ << "};" << endl;
+ }
+}
+
+std::string t_js_generator::render_recv_throw(std::string var) {
+ if (gen_node_) {
+ return "return callback(" + var + ");";
+ } else {
+ return "throw " + var + ";";
+ }
+}
+
+std::string t_js_generator::render_recv_return(std::string var) {
+ if (gen_node_) {
+ return "return callback(null, " + var + ");";
+ } else {
+ return "return " + var + ";";
+ }
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_js_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool inclass) {
+ (void)inclass;
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << name << " = input.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
+ break;
+ case t_base_type::TYPE_STRING:
+ out << (type->is_binary() ? "readBinary()" : "readString()");
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool()";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte()";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16()";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32()";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64()";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble()";
+ break;
+ default:
+ throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
+ }
+ } else if (type->is_enum()) {
+ out << "readI32()";
+ }
+
+ if (!gen_node_) {
+ out << ".value";
+ }
+
+ out << ";" << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a variable. This makes two key assumptions,
+ * first that there is a const char* variable named data that points to the
+ * buffer for deserialization, and that there is a variable protocol which
+ * is a reference to a TProtocol serialization object.
+ */
+void t_js_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ out << indent() << prefix << " = new " << js_type_namespace(tstruct->get_program())
+ << tstruct->get_name() << "();" << endl << indent() << prefix << ".read(input);" << endl;
+}
+
+void t_js_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ string size = tmp("_size");
+ string rtmp3 = tmp("_rtmp3");
+
+ t_field fsize(g_type_i32, size);
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ out << indent() << prefix << " = {};" << endl;
+
+ out << indent() << js_const_type_ << rtmp3 << " = input.readMapBegin();" << endl;
+ out << indent() << js_const_type_ << size << " = " << rtmp3 << ".size || 0;" << endl;
+
+ } else if (ttype->is_set()) {
+
+ out << indent() << prefix << " = [];" << endl
+ << indent() << js_const_type_ << rtmp3 << " = input.readSetBegin();" << endl
+ << indent() << js_const_type_ << size << " = " << rtmp3 << ".size || 0;" << endl;
+
+ } else if (ttype->is_list()) {
+
+ out << indent() << prefix << " = [];" << endl
+ << indent() << js_const_type_ << rtmp3 << " = input.readListBegin();" << endl
+ << indent() << js_const_type_ << size << " = " << rtmp3 << ".size || 0;" << endl;
+ }
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (" << js_let_type_ << i << " = 0; " << i << " < " << size << "; ++" << i << ") {" << endl;
+
+ indent_up();
+
+ if (ttype->is_map()) {
+ if (!gen_node_) {
+ out << indent() << "if (" << i << " > 0 ) {" << endl << indent()
+ << " if (input.rstack.length > input.rpos[input.rpos.length -1] + 1) {" << endl
+ << indent() << " input.rstack.pop();" << endl << indent() << " }" << endl << indent()
+ << "}" << endl;
+ }
+
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "input.readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "input.readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "input.readListEnd();" << endl;
+ }
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_js_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("key");
+ string val = tmp("val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey, false, false) << ";" << endl;
+ indent(out) << declare_field(&fval, false, false) << ";" << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+void t_js_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << js_let_type_ << elem << " = null;" << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".push(" << elem << ");" << endl;
+}
+
+void t_js_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << js_let_type_ << elem << " = null;" << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".push(" << elem << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_js_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw std::runtime_error("CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name());
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name());
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = tfield->get_name();
+
+ // Hack for when prefix is defined (always a hash ref)
+ if (!prefix.empty())
+ name = prefix + tfield->get_name();
+
+ indent(out) << "output.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw std::runtime_error("compiler error: cannot serialize void field in a struct: " + name);
+ break;
+ case t_base_type::TYPE_STRING:
+ out << (type->is_binary() ? "writeBinary(" : "writeString(") << name << ")";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ")";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ")";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ")";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ")";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ")";
+ break;
+ default:
+ throw std::runtime_error("compiler error: no JS name for base type " + t_base_type::t_base_name(tbase));
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ")";
+ }
+ out << ";" << endl;
+
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_js_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << prefix << ".write(output);" << endl;
+}
+
+/**
+ * Writes out a container
+ */
+void t_js_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ if (ttype->is_map()) {
+ indent(out) << "output.writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "Thrift.objectLength(" << prefix << "));" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "output.writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
+ << prefix << ".length);" << endl;
+
+ } else if (ttype->is_list()) {
+
+ indent(out) << "output.writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
+ << ", " << prefix << ".length);" << endl;
+ }
+
+ if (ttype->is_map()) {
+ string kiter = tmp("kiter");
+ string viter = tmp("viter");
+ indent(out) << "for (" << js_let_type_ << kiter << " in " << prefix << ") {" << endl;
+ indent_up();
+ indent(out) << "if (" << prefix << ".hasOwnProperty(" << kiter << ")) {" << endl;
+ indent_up();
+ indent(out) << js_let_type_ << viter << " = " << prefix << "[" << kiter << "];" << endl;
+ generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+ scope_down(out);
+ scope_down(out);
+
+ } else if (ttype->is_set()) {
+ string iter = tmp("iter");
+ indent(out) << "for (" << js_let_type_ << iter << " in " << prefix << ") {" << endl;
+ indent_up();
+ indent(out) << "if (" << prefix << ".hasOwnProperty(" << iter << ")) {" << endl;
+ indent_up();
+ indent(out) << iter << " = " << prefix << "[" << iter << "];" << endl;
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ scope_down(out);
+ scope_down(out);
+
+ } else if (ttype->is_list()) {
+ string iter = tmp("iter");
+ indent(out) << "for (" << js_let_type_ << iter << " in " << prefix << ") {" << endl;
+ indent_up();
+ indent(out) << "if (" << prefix << ".hasOwnProperty(" << iter << ")) {" << endl;
+ indent_up();
+ indent(out) << iter << " = " << prefix << "[" << iter << "];" << endl;
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ scope_down(out);
+ scope_down(out);
+ }
+
+ if (ttype->is_map()) {
+ indent(out) << "output.writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "output.writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "output.writeListEnd();" << endl;
+ }
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_js_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), kiter);
+ generate_serialize_field(out, &kfield);
+
+ t_field vfield(tmap->get_val_type(), viter);
+ generate_serialize_field(out, &vfield);
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_js_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield);
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_js_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield);
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_js_generator::declare_field(t_field* tfield, bool init, bool obj) {
+ string result = "this." + tfield->get_name();
+
+ if (!obj) {
+ result = js_let_type_ + tfield->get_name();
+ }
+
+ if (init) {
+ t_type* type = get_true_type(tfield->get_type());
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ break;
+ case t_base_type::TYPE_STRING:
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ result += " = null";
+ break;
+ default:
+ throw std::runtime_error("compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase));
+ }
+ } else if (type->is_enum()) {
+ result += " = null";
+ } else if (type->is_map()) {
+ result += " = null";
+ } else if (type->is_container()) {
+ result += " = null";
+ } else if (type->is_struct() || type->is_xception()) {
+ if (obj) {
+ result += " = new " + js_type_namespace(type->get_program()) + type->get_name() + "()";
+ } else {
+ result += " = null";
+ }
+ }
+ } else {
+ result += " = null";
+ }
+ return result;
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_js_generator::function_signature(t_function* tfunction,
+ string prefix,
+ bool include_callback) {
+
+ string str;
+
+ str = prefix + tfunction->get_name() + " = function(";
+
+ str += argument_list(tfunction->get_arglist(), include_callback);
+
+ str += ")";
+ return str;
+}
+
+/**
+ * Renders a field list
+ */
+string t_js_generator::argument_list(t_struct* tstruct, bool include_callback) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += (*f_iter)->get_name();
+ }
+
+ if (include_callback) {
+ if (!fields.empty()) {
+ result += ", ";
+ }
+ result += "callback";
+ }
+
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_js_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw std::runtime_error("NO T_VOID CONSTRUCT");
+ case t_base_type::TYPE_STRING:
+ return "Thrift.Type.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "Thrift.Type.BOOL";
+ case t_base_type::TYPE_I8:
+ return "Thrift.Type.BYTE";
+ case t_base_type::TYPE_I16:
+ return "Thrift.Type.I16";
+ case t_base_type::TYPE_I32:
+ return "Thrift.Type.I32";
+ case t_base_type::TYPE_I64:
+ return "Thrift.Type.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "Thrift.Type.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "Thrift.Type.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "Thrift.Type.STRUCT";
+ } else if (type->is_map()) {
+ return "Thrift.Type.MAP";
+ } else if (type->is_set()) {
+ return "Thrift.Type.SET";
+ } else if (type->is_list()) {
+ return "Thrift.Type.LIST";
+ }
+
+ throw std::runtime_error("INVALID TYPE IN type_to_enum: " + type->get_name());
+}
+
+/**
+ * Converts a t_type to a TypeScript type (string).
+ * @param t_type Type to convert to TypeScript
+ * @return String TypeScript type
+ */
+string t_js_generator::ts_get_type(t_type* type) {
+ std::string ts_type;
+
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ ts_type = "string";
+ break;
+ case t_base_type::TYPE_BOOL:
+ ts_type = "boolean";
+ break;
+ case t_base_type::TYPE_I8:
+ ts_type = "any";
+ break;
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_DOUBLE:
+ ts_type = "number";
+ break;
+ case t_base_type::TYPE_I64:
+ ts_type = "Int64";
+ break;
+ case t_base_type::TYPE_VOID:
+ ts_type = "void";
+ }
+ } else if (type->is_enum() || type->is_struct() || type->is_xception()) {
+ std::string type_name;
+
+ if (type->get_program()) {
+ type_name = js_namespace(type->get_program());
+
+ // If the type is not defined within the current program, we need to prefix it with the same name as
+ // the generated "import" statement for the types containing program
+ if(type->get_program() != program_) {
+ auto prefix = include_2_import_name.find(type->get_program());
+
+ if(prefix != include_2_import_name.end()) {
+ type_name.append(prefix->second);
+ type_name.append(".");
+ }
+ }
+ }
+
+ type_name.append(type->get_name());
+ ts_type = type_name;
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+
+ ts_type = ts_get_type(etype) + "[]";
+ } else if (type->is_map()) {
+ string ktype = ts_get_type(((t_map*)type)->get_key_type());
+ string vtype = ts_get_type(((t_map*)type)->get_val_type());
+
+
+ if (ktype == "number" || ktype == "string" ) {
+ ts_type = "{ [k: " + ktype + "]: " + vtype + "; }";
+ } else if ((((t_map*)type)->get_key_type())->is_enum()) {
+ // Not yet supported (enum map): https://github.com/Microsoft/TypeScript/pull/2652
+ //ts_type = "{ [k: " + ktype + "]: " + vtype + "; }";
+ ts_type = "{ [k: number /*" + ktype + "*/]: " + vtype + "; }";
+ } else {
+ ts_type = "any";
+ }
+ }
+
+ return ts_type;
+}
+
+/**
+ * Renders a TypeScript function signature of the form 'name(args: types): type;'
+ *
+ * @param t_function Function definition
+ * @param bool in-/exclude the callback argument
+ * @return String of rendered function definition
+ */
+std::string t_js_generator::ts_function_signature(t_function* tfunction, bool include_callback) {
+ string str;
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ str = tfunction->get_name() + "(";
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ str += (*f_iter)->get_name() + ts_get_req(*f_iter) + ": " + ts_get_type((*f_iter)->get_type());
+
+ if (f_iter + 1 != fields.end() || (include_callback && fields.size() > 0)) {
+ str += ", ";
+ }
+ }
+
+ if (include_callback) {
+ if (gen_node_) {
+ t_struct* exceptions = tfunction->get_xceptions();
+ string exception_types;
+ if (exceptions) {
+ const vector<t_field*>& members = exceptions->get_members();
+ for (vector<t_field*>::const_iterator it = members.begin(); it != members.end(); ++it) {
+ t_type* t = get_true_type((*it)->get_type());
+ if (it == members.begin()) {
+ exception_types = js_type_namespace(t->get_program()) + t->get_name();
+ } else {
+ exception_types += " | " + js_type_namespace(t->get_program()) + t->get_name();
+ }
+ }
+ }
+ if (exception_types == "") {
+ str += "callback?: (error: void, response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): ";
+ } else {
+ str += "callback?: (error: " + exception_types + ", response: " + ts_get_type(tfunction->get_returntype()) + ")=>void): ";
+ }
+ } else {
+ str += "callback?: (data: " + ts_get_type(tfunction->get_returntype()) + ")=>void): ";
+ }
+
+ if (gen_jquery_) {
+ str += "JQueryPromise<" + ts_get_type(tfunction->get_returntype()) +">;";
+ } else {
+ str += "void;";
+ }
+ } else {
+ if (gen_es6_) {
+ str += "): Promise<" + ts_get_type(tfunction->get_returntype()) + ">;";
+ }
+ else {
+ str += "): " + ts_get_type(tfunction->get_returntype()) + ";";
+ }
+ }
+
+ return str;
+}
+
+/**
+ * Takes a name and produces a valid NodeJS identifier from it
+ *
+ * @param name The name which shall become a valid NodeJS identifier
+ * @return The modified name with the updated identifier
+ */
+std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name) {
+ std::string str = name;
+ if (str.empty()) {
+ return str;
+ }
+
+ // tests rely on this
+ assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+ // if the first letter is a number, we add an additional underscore in front of it
+ char c = str.at(0);
+ if (('0' <= c) && (c <= '9')) {
+ str = "_" + str;
+ }
+
+ // following chars: letter, number or underscore
+ for (size_t i = 0; i < str.size(); ++i) {
+ c = str.at(i);
+ if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
+ && ('_' != c) && ('$' != c)) {
+ str.replace(i, 1, "_");
+ }
+ }
+
+ return str;
+}
+
+void t_js_generator::parse_imports(t_program* program, const std::string& imports_string) {
+ if (program->get_recursive()) {
+ throw std::invalid_argument("[-gen js:imports=] option is not usable in recursive code generation mode");
+ }
+ std::stringstream sstream(imports_string);
+ std::string import;
+ while (std::getline(sstream, import, ':')) {
+ imports.emplace_back(import);
+ }
+ if (imports.empty()) {
+ throw std::invalid_argument("invalid usage: [-gen js:imports=] requires at least one path "
+ "(multiple paths are separated by ':')");
+ }
+ for (auto& import : imports) {
+ // Strip trailing '/'
+ if (!import.empty() && import[import.size() - 1] == '/') {
+ import = import.substr(0, import.size() - 1);
+ }
+ if (import.empty()) {
+ throw std::invalid_argument("empty paths are not allowed in imports");
+ }
+ std::ifstream episode_file;
+ string line;
+ const auto episode_file_path = import + "/" + episode_file_name;
+ episode_file.open(episode_file_path);
+ if (!episode_file) {
+ throw std::runtime_error("failed to open the file '" + episode_file_path + "'");
+ }
+ while (std::getline(episode_file, line)) {
+ const auto separator_position = line.find(':');
+ if (separator_position == string::npos) {
+ // could not find the separator in the line
+ throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the line '" + line
+ + "' does not have a key:value separator ':'");
+ }
+ const auto module_name = line.substr(0, separator_position);
+ const auto import_path = line.substr(separator_position + 1);
+ if (module_name.empty()) {
+ throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the module name is empty");
+ }
+ if (import_path.empty()) {
+ throw std::runtime_error("the episode file '" + episode_file_path + "' is malformed, the import path is empty");
+ }
+ const auto module_import_path = import.substr(import.find_last_of('/') + 1) + "/" + import_path;
+ const auto result = module_name_2_import_path.emplace(module_name, module_import_path);
+ if (!result.second) {
+ throw std::runtime_error("multiple providers of import path found for " + module_name
+ + "\n\t" + module_import_path + "\n\t" + result.first->second);
+ }
+ }
+ }
+}
+void t_js_generator::parse_thrift_package_output_directory(const std::string& thrift_package_output_directory) {
+ thrift_package_output_directory_ = thrift_package_output_directory;
+ // Strip trailing '/'
+ if (!thrift_package_output_directory_.empty() && thrift_package_output_directory_[thrift_package_output_directory_.size() - 1] == '/') {
+ thrift_package_output_directory_ = thrift_package_output_directory_.substr(0, thrift_package_output_directory_.size() - 1);
+ }
+ // Check that the thrift_package_output_directory is not empty after stripping
+ if (thrift_package_output_directory_.empty()) {
+ throw std::invalid_argument("the thrift_package_output_directory argument must not be empty");
+ } else {
+ gen_episode_file_ = true;
+ }
+}
+
+THRIFT_REGISTER_GENERATOR(js,
+ "Javascript",
+ " jquery: Generate jQuery compatible code.\n"
+ " node: Generate node.js compatible code.\n"
+ " ts: Generate TypeScript definition files.\n"
+ " with_ns: Create global namespace objects when using node.js\n"
+ " es6: Create ES6 code with Promises\n"
+ " thrift_package_output_directory=<path>:\n"
+ " Generate episode file and use the <path> as prefix\n"
+ " imports=<paths_to_modules>:\n"
+ " ':' separated list of paths of modules that has episode files in their root\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc
new file mode 100644
index 000000000..eb4ef790b
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc
@@ -0,0 +1,793 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <limits>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+using std::stack;
+
+static const string endl = "\n";
+static const string quot = "\"";
+static const bool NO_INDENT = false;
+static const bool FORCE_STRING = true;
+
+class t_json_generator : public t_generator {
+public:
+ t_json_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ should_merge_includes_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("merge") == 0) {
+ should_merge_includes_ = true;
+ } else {
+ throw "unknown option json:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-json";
+ }
+
+ ~t_json_generator() override = default;
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_program() override;
+ void generate_function(t_function* tfunc);
+ void generate_field(t_field* field);
+
+ void generate_service(t_service* tservice) override;
+ void generate_struct(t_struct* tstruct) override;
+
+private:
+ bool should_merge_includes_;
+
+ ofstream_with_content_based_conditional_update f_json_;
+ std::stack<bool> comma_needed_;
+
+ template <typename T>
+ string number_to_string(T t) {
+ std::ostringstream out;
+ out.imbue(std::locale::classic());
+ out.precision(std::numeric_limits<T>::digits10);
+ out << t;
+ return out.str();
+ }
+
+ template <typename T>
+ void write_number(T n) {
+ f_json_ << number_to_string(n);
+ }
+
+ string get_type_name(t_type* ttype);
+ string get_qualified_name(t_type* ttype);
+
+ void start_object(bool should_indent = true);
+ void start_array();
+ void end_object();
+ void end_array();
+ void write_comma_if_needed();
+ void indicate_comma_needed();
+ string escape_json_string(const string& input);
+ string json_str(const string& str);
+ void merge_includes(t_program*);
+
+ void generate_constant(t_const* con);
+
+ void write_type_spec_entry(const char* name, t_type* ttype);
+ void write_type_spec_object(const char* name, t_type* ttype);
+ void write_type_spec(t_type* ttype);
+ void write_string(const string& value);
+ void write_value(t_type* tvalue);
+ void write_const_value(t_const_value* value, bool force_string = false);
+ void write_key_and(string key);
+ void write_key_and_string(string key, string val);
+ void write_key_and_integer(string key, int val);
+ void write_key_and_bool(string key, bool val);
+};
+
+void t_json_generator::init_generator() {
+ MKDIR(get_out_dir().c_str());
+
+ string f_json_name = get_out_dir() + program_->get_name() + ".json";
+ f_json_.open(f_json_name.c_str());
+
+ // Merge all included programs into this one so we can output one big file.
+ if (should_merge_includes_) {
+ merge_includes(program_);
+ }
+}
+
+string t_json_generator::escape_json_string(const string& input) {
+ std::ostringstream ss;
+ for (char iter : input) {
+ switch (iter) {
+ case '\\':
+ ss << "\\\\";
+ break;
+ case '"':
+ ss << "\\\"";
+ break;
+ case '/':
+ ss << "\\/";
+ break;
+ case '\b':
+ ss << "\\b";
+ break;
+ case '\f':
+ ss << "\\f";
+ break;
+ case '\n':
+ ss << "\\n";
+ break;
+ case '\r':
+ ss << "\\r";
+ break;
+ case '\t':
+ ss << "\\t";
+ break;
+ default:
+ ss << iter;
+ break;
+ }
+ }
+ return ss.str();
+}
+
+void t_json_generator::start_object(bool should_indent) {
+ f_json_ << (should_indent ? indent() : "") << "{" << endl;
+ indent_up();
+ comma_needed_.push(false);
+}
+
+void t_json_generator::start_array() {
+ f_json_ << "[" << endl;
+ indent_up();
+ comma_needed_.push(false);
+}
+
+void t_json_generator::write_comma_if_needed() {
+ if (comma_needed_.top()) {
+ f_json_ << "," << endl;
+ }
+}
+
+void t_json_generator::indicate_comma_needed() {
+ comma_needed_.pop();
+ comma_needed_.push(true);
+}
+
+void t_json_generator::write_key_and(string key) {
+ write_comma_if_needed();
+ indent(f_json_) << json_str(key) << ": ";
+ indicate_comma_needed();
+}
+
+void t_json_generator::write_key_and_integer(string key, int val) {
+ write_comma_if_needed();
+ indent(f_json_) << json_str(key) << ": " << number_to_string(val);
+ indicate_comma_needed();
+}
+
+void t_json_generator::write_key_and_string(string key, string val) {
+ write_comma_if_needed();
+ indent(f_json_) << json_str(key) << ": " << json_str(val);
+ indicate_comma_needed();
+}
+
+void t_json_generator::write_key_and_bool(string key, bool val) {
+ write_comma_if_needed();
+ indent(f_json_) << json_str(key) << ": " << (val ? "true" : "false");
+ indicate_comma_needed();
+}
+
+void t_json_generator::end_object() {
+ indent_down();
+ f_json_ << endl << indent() << "}";
+ comma_needed_.pop();
+}
+
+void t_json_generator::end_array() {
+ indent_down();
+ if (comma_needed_.top()) {
+ f_json_ << endl;
+ }
+ indent(f_json_) << "]";
+ comma_needed_.pop();
+}
+
+void t_json_generator::write_type_spec_object(const char* name, t_type* ttype) {
+ ttype = ttype->get_true_type();
+ if (ttype->is_struct() || ttype->is_xception() || ttype->is_container()) {
+ write_key_and(name);
+ start_object(NO_INDENT);
+ write_key_and("typeId");
+ write_type_spec(ttype);
+ end_object();
+ }
+}
+
+void t_json_generator::write_type_spec_entry(const char* name, t_type* ttype) {
+ write_key_and(name);
+ write_type_spec(ttype);
+}
+
+void t_json_generator::write_type_spec(t_type* ttype) {
+ ttype = ttype->get_true_type();
+
+ write_string(get_type_name(ttype));
+
+ if (ttype->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : ttype->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+
+ if (ttype->is_struct() || ttype->is_xception()) {
+ write_key_and_string("class", get_qualified_name(ttype));
+ } else if (ttype->is_map()) {
+ t_type* ktype = ((t_map*)ttype)->get_key_type();
+ t_type* vtype = ((t_map*)ttype)->get_val_type();
+ write_key_and_string("keyTypeId", get_type_name(ktype));
+ write_key_and_string("valueTypeId", get_type_name(vtype));
+ write_type_spec_object("keyType", ktype);
+ write_type_spec_object("valueType", vtype);
+ } else if (ttype->is_list()) {
+ t_type* etype = ((t_list*)ttype)->get_elem_type();
+ write_key_and_string("elemTypeId", get_type_name(etype));
+ write_type_spec_object("elemType", etype);
+ } else if (ttype->is_set()) {
+ t_type* etype = ((t_set*)ttype)->get_elem_type();
+ write_key_and_string("elemTypeId", get_type_name(etype));
+ write_type_spec_object("elemType", etype);
+ }
+}
+
+void t_json_generator::close_generator() {
+ f_json_ << endl;
+ f_json_.close();
+}
+
+void t_json_generator::merge_includes(t_program* program) {
+ vector<t_program*> includes = program->get_includes();
+ vector<t_program*>::iterator inc_iter;
+ for (inc_iter = includes.begin(); inc_iter != includes.end(); ++inc_iter) {
+ t_program* include = *inc_iter;
+ // recurse in case we get crazy
+ merge_includes(include);
+ // merge enums
+ vector<t_enum*> enums = include->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ program->add_enum(*en_iter);
+ }
+ // merge typedefs
+ vector<t_typedef*> typedefs = include->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ program->add_typedef(*td_iter);
+ }
+ // merge structs
+ vector<t_struct*> objects = include->get_objects();
+ vector<t_struct*>::iterator o_iter;
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ program->add_struct(*o_iter);
+ }
+ // merge constants
+ vector<t_const*> consts = include->get_consts();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ program->add_const(*c_iter);
+ }
+
+ // merge services
+ vector<t_service*> services = include->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ program->add_service(*sv_iter);
+ }
+ }
+}
+
+void t_json_generator::generate_program() {
+
+ init_generator();
+
+ start_object();
+ write_key_and_string("name", program_->get_name());
+ if (program_->has_doc()) {
+ write_key_and_string("doc", program_->get_doc());
+ }
+
+ // When merging includes, the "namespaces" and "includes" sections
+ // become ambiguous, so just skip them.
+ if (!should_merge_includes_) {
+ // Generate namespaces
+ write_key_and("namespaces");
+ start_object(NO_INDENT);
+ const map<string, string>& namespaces = program_->get_namespaces();
+ map<string, string>::const_iterator ns_it;
+ for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) {
+ write_key_and_string(ns_it->first, ns_it->second);
+ indicate_comma_needed();
+ }
+ end_object();
+
+ // Generate includes
+ write_key_and("includes");
+ start_array();
+ const vector<t_program*> includes = program_->get_includes();
+ vector<t_program*>::const_iterator inc_it;
+ for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) {
+ write_comma_if_needed();
+ write_string((*inc_it)->get_name());
+ indicate_comma_needed();
+ }
+ end_array();
+ }
+
+ // Generate enums
+ write_key_and("enums");
+ start_array();
+ vector<t_enum*> enums = program_->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ write_comma_if_needed();
+ generate_enum(*en_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ // Generate typedefs
+ write_key_and("typedefs");
+ start_array();
+ vector<t_typedef*> typedefs = program_->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ write_comma_if_needed();
+ generate_typedef(*td_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ // Generate structs, exceptions, and unions in declared order
+ write_key_and("structs");
+ start_array();
+ vector<t_struct*> objects = program_->get_objects();
+ vector<t_struct*>::iterator o_iter;
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ write_comma_if_needed();
+ if ((*o_iter)->is_xception()) {
+ generate_xception(*o_iter);
+ } else {
+ generate_struct(*o_iter);
+ }
+ indicate_comma_needed();
+ }
+ end_array();
+
+ // Generate constants
+ write_key_and("constants");
+ start_array();
+ vector<t_const*> consts = program_->get_consts();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ write_comma_if_needed();
+ generate_constant(*c_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ // Generate services
+ write_key_and("services");
+ start_array();
+ vector<t_service*> services = program_->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ write_comma_if_needed();
+ generate_service(*sv_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ end_object();
+
+ // Close the generator
+ close_generator();
+}
+
+void t_json_generator::generate_typedef(t_typedef* ttypedef) {
+ start_object();
+ write_key_and_string("name", get_qualified_name(ttypedef));
+ write_key_and_string("typeId", get_type_name(ttypedef->get_true_type()));
+ write_type_spec_object("type", ttypedef->get_true_type());
+ if (ttypedef->has_doc()) {
+ write_key_and_string("doc", ttypedef->get_doc());
+ }
+ if (ttypedef->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : ttypedef->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+ end_object();
+}
+
+void t_json_generator::write_string(const string& value) {
+ f_json_ << quot << escape_json_string(value) << quot;
+}
+
+void t_json_generator::write_const_value(t_const_value* value, bool should_force_string) {
+
+ switch (value->get_type()) {
+
+ case t_const_value::CV_IDENTIFIER:
+ case t_const_value::CV_INTEGER:
+ if (should_force_string) {
+ write_string(number_to_string(value->get_integer()));
+ } else {
+ write_number(value->get_integer());
+ }
+ break;
+
+ case t_const_value::CV_DOUBLE:
+ if (should_force_string) {
+ write_string(number_to_string(value->get_double()));
+ } else {
+ write_number(value->get_double());
+ }
+ break;
+
+ case t_const_value::CV_STRING:
+ write_string(value->get_string());
+ break;
+
+ case t_const_value::CV_LIST: {
+ start_array();
+ std::vector<t_const_value*> list = value->get_list();
+ std::vector<t_const_value*>::iterator lit;
+ for (lit = list.begin(); lit != list.end(); ++lit) {
+ write_comma_if_needed();
+ f_json_ << indent();
+ write_const_value(*lit);
+ indicate_comma_needed();
+ }
+ end_array();
+ break;
+ }
+
+ case t_const_value::CV_MAP: {
+ start_object(NO_INDENT);
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare> map = value->get_map();
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator mit;
+ for (mit = map.begin(); mit != map.end(); ++mit) {
+ write_comma_if_needed();
+ f_json_ << indent();
+ // JSON objects only allow string keys
+ write_const_value(mit->first, FORCE_STRING);
+ f_json_ << ": ";
+ write_const_value(mit->second);
+ indicate_comma_needed();
+ }
+ end_object();
+ break;
+ }
+
+ default:
+ f_json_ << "null";
+ break;
+ }
+}
+
+string t_json_generator::json_str(const string& str) {
+ return quot + escape_json_string(str) + quot;
+}
+
+void t_json_generator::generate_constant(t_const* con) {
+ start_object();
+
+ write_key_and_string("name", con->get_name());
+ write_key_and_string("typeId", get_type_name(con->get_type()));
+ write_type_spec_object("type", con->get_type());
+
+ if (con->has_doc()) {
+ write_key_and_string("doc", con->get_doc());
+ }
+
+ write_key_and("value");
+ write_const_value(con->get_value());
+
+ end_object();
+}
+
+void t_json_generator::generate_enum(t_enum* tenum) {
+ start_object();
+
+ write_key_and_string("name", tenum->get_name());
+
+ if (tenum->has_doc()) {
+ write_key_and_string("doc", tenum->get_doc());
+ }
+
+ if (tenum->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : tenum->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+
+ write_key_and("members");
+ start_array();
+ vector<t_enum_value*> values = tenum->get_constants();
+ vector<t_enum_value*>::iterator val_iter;
+ for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
+ write_comma_if_needed();
+ t_enum_value* val = (*val_iter);
+ start_object();
+ write_key_and_string("name", val->get_name());
+ write_key_and_integer("value", val->get_value());
+ if (val->has_doc()) {
+ write_key_and_string("doc", val->get_doc());
+ }
+ end_object();
+ indicate_comma_needed();
+ }
+ end_array();
+
+ end_object();
+}
+
+void t_json_generator::generate_struct(t_struct* tstruct) {
+ start_object();
+
+ write_key_and_string("name", tstruct->get_name());
+
+ if (tstruct->has_doc()) {
+ write_key_and_string("doc", tstruct->get_doc());
+ }
+
+ if (tstruct->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : tstruct->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+
+ write_key_and_bool("isException", tstruct->is_xception());
+
+ write_key_and_bool("isUnion", tstruct->is_union());
+
+ write_key_and("fields");
+ start_array();
+ vector<t_field*> members = tstruct->get_members();
+ vector<t_field*>::iterator mem_iter;
+ for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) {
+ write_comma_if_needed();
+ generate_field(*mem_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ end_object();
+}
+
+void t_json_generator::generate_service(t_service* tservice) {
+ start_object();
+
+ write_key_and_string("name", get_qualified_name(tservice));
+
+ if (tservice->get_extends()) {
+ write_key_and_string("extends", get_qualified_name(tservice->get_extends()));
+ }
+
+ if (tservice->has_doc()) {
+ write_key_and_string("doc", tservice->get_doc());
+ }
+
+ if (tservice->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : tservice->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+
+ write_key_and("functions");
+ start_array();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator fn_iter = functions.begin();
+ for (; fn_iter != functions.end(); fn_iter++) {
+ write_comma_if_needed();
+ generate_function(*fn_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ end_object();
+}
+
+void t_json_generator::generate_function(t_function* tfunc) {
+ start_object();
+
+ write_key_and_string("name", tfunc->get_name());
+
+ write_key_and_string("returnTypeId", get_type_name(tfunc->get_returntype()));
+ write_type_spec_object("returnType", tfunc->get_returntype());
+
+ write_key_and_bool("oneway", tfunc->is_oneway());
+
+ if (tfunc->has_doc()) {
+ write_key_and_string("doc", tfunc->get_doc());
+ }
+
+ if (tfunc->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : tfunc->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+
+ write_key_and("arguments");
+ start_array();
+ vector<t_field*> members = tfunc->get_arglist()->get_members();
+ vector<t_field*>::iterator mem_iter = members.begin();
+ for (; mem_iter != members.end(); mem_iter++) {
+ write_comma_if_needed();
+ generate_field(*mem_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ write_key_and("exceptions");
+ start_array();
+ vector<t_field*> excepts = tfunc->get_xceptions()->get_members();
+ vector<t_field*>::iterator ex_iter = excepts.begin();
+ for (; ex_iter != excepts.end(); ex_iter++) {
+ write_comma_if_needed();
+ generate_field(*ex_iter);
+ indicate_comma_needed();
+ }
+ end_array();
+
+ end_object();
+}
+
+void t_json_generator::generate_field(t_field* field) {
+ start_object();
+
+ write_key_and_integer("key", field->get_key());
+ write_key_and_string("name", field->get_name());
+ write_key_and_string("typeId", get_type_name(field->get_type()));
+ write_type_spec_object("type", field->get_type());
+
+ if (field->has_doc()) {
+ write_key_and_string("doc", field->get_doc());
+ }
+
+ if (field->annotations_.size() > 0) {
+ write_key_and("annotations");
+ start_object();
+ for (auto & annotation : field->annotations_) {
+ write_key_and_string(annotation.first, annotation.second);
+ }
+ end_object();
+ }
+
+ write_key_and("required");
+ switch (field->get_req()) {
+ case t_field::T_REQUIRED:
+ write_string("required");
+ break;
+ case t_field::T_OPT_IN_REQ_OUT:
+ write_string("req_out");
+ break;
+ default:
+ write_string("optional");
+ break;
+ }
+
+ if (field->get_value()) {
+ write_key_and("default");
+ write_const_value(field->get_value());
+ }
+
+ end_object();
+}
+
+string t_json_generator::get_type_name(t_type* ttype) {
+ ttype = ttype->get_true_type();
+ if (ttype->is_list()) {
+ return "list";
+ }
+ if (ttype->is_set()) {
+ return "set";
+ }
+ if (ttype->is_map()) {
+ return "map";
+ }
+ if (ttype->is_enum()) {
+ return "i32";
+ }
+ if (ttype->is_struct()) {
+ return ((t_struct*)ttype)->is_union() ? "union" : "struct";
+ }
+ if (ttype->is_xception()) {
+ return "exception";
+ }
+ if (ttype->is_base_type()) {
+ t_base_type* tbasetype = (t_base_type*)ttype;
+ return tbasetype->is_binary() ? "binary" : t_base_type::t_base_name(tbasetype->get_base());
+ }
+
+ return "(unknown)";
+}
+
+string t_json_generator::get_qualified_name(t_type* ttype) {
+ if (should_merge_includes_ || ttype->get_program() == program_) {
+ return ttype->get_name();
+ }
+ return ttype->get_program()->get_name() + "." + ttype->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(json,
+ "JSON",
+ " merge: Generate output with included files merged\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc
new file mode 100644
index 000000000..e6432c898
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc
@@ -0,0 +1,1138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::ostream;
+using std::string;
+using std::vector;
+using std::map;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * LUA code generator.
+ *
+ */
+class t_lua_generator : public t_oop_generator {
+public:
+ t_lua_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ gen_requires_ = true;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("omit_requires") == 0) {
+ gen_requires_ = false;
+ } else {
+ throw "unknown option lua:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-lua";
+ }
+
+ /**
+ * Init and close methods
+ */
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+private:
+ /**
+ * True iff we should generate lua require statements.
+ */
+ bool gen_requires_;
+
+ /**
+ * Struct-level generation functions
+ */
+ void generate_lua_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false);
+ void generate_lua_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_lua_struct_writer(std::ostream& out, t_struct* tstruct);
+
+ /**
+ * Service-level generation functions
+ */
+ void generate_service_client(std::ostream& out, t_service* tservice);
+ void generate_service_interface(std::ostream& out, t_service* tservice);
+ void generate_service_processor(std::ostream& out, t_service* tservice);
+ void generate_process_function(std::ostream& out, t_service* tservice, t_function* tfunction);
+ void generate_service_helpers(ostream& out, t_service* tservice);
+ void generate_function_helpers(ostream& out, t_function* tfunction);
+
+ /**
+ * Deserialization (Read)
+ */
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ bool local,
+ std::string prefix = "");
+
+ void generate_deserialize_struct(std::ostream& out,
+ t_struct* tstruct,
+ bool local,
+ std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out,
+ t_type* ttype,
+ bool local,
+ std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ /**
+ * Serialization (Write)
+ */
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ /**
+ * Helper rendering functions
+ */
+ std::string lua_includes();
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct, std::string prefix = "");
+ std::string type_to_enum(t_type* ttype);
+ static std::string get_namespace(const t_program* program);
+
+ std::string autogen_comment() override {
+ return std::string("--\n") + "-- Autogenerated by Thrift\n" + "--\n"
+ + "-- DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "-- @"
+ "generated\n"
+ + "--\n";
+ }
+
+ /**
+ * File streams
+ */
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_consts_;
+ ofstream_with_content_based_conditional_update f_service_;
+};
+
+/**
+ * Init and close methods
+ */
+void t_lua_generator::init_generator() {
+ // Make output directory
+ string outdir = get_out_dir();
+ MKDIR(outdir.c_str());
+
+ // Make output files
+ string cur_namespace = get_namespace(program_);
+ string f_consts_name = outdir + cur_namespace + "constants.lua";
+ f_consts_.open(f_consts_name.c_str());
+ string f_types_name = outdir + cur_namespace + "ttypes.lua";
+ f_types_.open(f_types_name.c_str());
+
+ // Add headers
+ f_consts_ << autogen_comment() << lua_includes();
+ f_types_ << autogen_comment() << lua_includes();
+ if (gen_requires_) {
+ f_types_ << endl << "require '" << cur_namespace << "constants'";
+ }
+}
+
+void t_lua_generator::close_generator() {
+ // Close types file
+ f_types_.close();
+ f_consts_.close();
+}
+
+/**
+ * Generate a typedef (essentially a constant)
+ */
+void t_lua_generator::generate_typedef(t_typedef* ttypedef) {
+ f_types_ << endl << endl << indent() << ttypedef->get_symbolic() << " = "
+ << ttypedef->get_type()->get_name();
+}
+
+/**
+ * Generates code for an enumerated type (table)
+ */
+void t_lua_generator::generate_enum(t_enum* tenum) {
+ f_types_ << endl << endl << tenum->get_name() << " = {" << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end();) {
+ int32_t value = (*c_iter)->get_value();
+
+ f_types_ << " " << (*c_iter)->get_name() << " = " << value;
+ ++c_iter;
+ if (c_iter != constants.end()) {
+ f_types_ << ",";
+ }
+ f_types_ << endl;
+ }
+ f_types_ << "}";
+}
+
+/**
+ * Generate a constant (non-local) value
+ */
+void t_lua_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ f_consts_ << endl << endl << name << " = ";
+ f_consts_ << render_const_value(type, value);
+}
+
+/**
+ * Prints the value of a constant with the given type.
+ */
+string t_lua_generator::render_const_value(t_type* type, t_const_value* value) {
+ std::ostringstream out;
+
+ type = get_true_type(type);
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << "'" << value->get_string() << "'";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I64:
+ out << "lualongnumber.new('" << value->get_string() << "')";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << type->get_name() << " = {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end();) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ indent(out);
+ out << render_const_value(g_type_string, v_iter->first);
+ out << " = ";
+ out << render_const_value(field_type, v_iter->second);
+ ++v_iter;
+ if (v_iter != val.end()) {
+ out << ",";
+ }
+ }
+
+ out << "}";
+ indent_down();
+ } else if (type->is_map()) {
+ out << type->get_name() << "{" << endl;
+ indent_up();
+
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end();) {
+ indent(out) << "[" << render_const_value(ktype, v_iter->first)
+ << "] = " << render_const_value(vtype, v_iter->second);
+ ++v_iter;
+ if (v_iter != val.end()) {
+ out << ",";
+ }
+ out << endl;
+ }
+ indent_down();
+ indent(out) << "}";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ out << type->get_name() << " = {" << endl;
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end();) {
+ indent(out);
+ out << "[" << render_const_value(etype, *v_iter) << "]";
+ if (type->is_set()) {
+ out << " = true";
+ } else {
+ out << " = false";
+ }
+ ++v_iter;
+ if (v_iter != val.end()) {
+ out << "," << endl;
+ }
+ }
+ out << "}";
+ }
+ return out.str();
+}
+
+/**
+ * Generate a thrift struct
+ */
+void t_lua_generator::generate_struct(t_struct* tstruct) {
+ generate_lua_struct_definition(f_types_, tstruct, false);
+}
+
+/**
+ * Generate a thrift exception
+ */
+void t_lua_generator::generate_xception(t_struct* txception) {
+ generate_lua_struct_definition(f_types_, txception, true);
+}
+
+/**
+ * Generate a thrift struct or exception (lua table)
+ */
+void t_lua_generator::generate_lua_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ vector<t_field*>::const_iterator m_iter;
+ const vector<t_field*>& members = tstruct->get_members();
+
+ indent(out) << endl << endl << tstruct->get_name();
+ if (is_exception) {
+ out << " = TException:new{" << endl << indent() << " __type = '" << tstruct->get_name() << "'";
+ if (members.size() > 0) {
+ out << ",";
+ }
+ out << endl;
+ } else {
+ out << " = __TObject:new{" << endl;
+ }
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end();) {
+ indent(out);
+ out << (*m_iter)->get_name();
+ ++m_iter;
+ if (m_iter != members.end()) {
+ out << "," << endl;
+ }
+ }
+ indent_down();
+ indent(out);
+ out << endl << "}";
+
+ generate_lua_struct_reader(out, tstruct);
+ generate_lua_struct_writer(out, tstruct);
+}
+
+/**
+ * Generate a struct/exception reader
+ */
+void t_lua_generator::generate_lua_struct_reader(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // function
+ indent(out) << endl << endl << "function " << tstruct->get_name() << ":read(iprot)" << endl;
+ indent_up();
+
+ indent(out) << "iprot:readStructBegin()" << endl;
+
+ // while: Read in fields
+ indent(out) << "while true do" << endl;
+ indent_up();
+
+ // if: Check what to read
+ indent(out) << "local fname, ftype, fid = iprot:readFieldBegin()" << endl;
+ indent(out) << "if ftype == TType.STOP then" << endl;
+ indent_up();
+ indent(out) << "break" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent_down();
+ indent(out) << "elseif fid == " << (*f_iter)->get_key() << " then" << endl;
+ indent_up();
+ indent(out) << "if ftype == " << type_to_enum((*f_iter)->get_type()) << " then" << endl;
+ indent_up();
+
+ // Read field contents
+ generate_deserialize_field(out, *f_iter, false, "self.");
+
+ indent_down();
+ indent(out) << "else" << endl;
+ indent(out) << " iprot:skip(ftype)" << endl;
+ indent(out) << "end" << endl;
+ }
+
+ // end if
+ indent_down();
+ indent(out) << "else" << endl;
+ indent(out) << " iprot:skip(ftype)" << endl;
+ indent(out) << "end" << endl;
+ indent(out) << "iprot:readFieldEnd()" << endl;
+
+ // end while
+ indent_down();
+ indent(out) << "end" << endl;
+ indent(out) << "iprot:readStructEnd()" << endl;
+
+ // end function
+ indent_down();
+ indent(out);
+ out << "end";
+}
+
+/**
+ * Generate a struct/exception writer
+ */
+void t_lua_generator::generate_lua_struct_writer(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // function
+ indent(out) << endl << endl << "function " << tstruct->get_name() << ":write(oprot)" << endl;
+ indent_up();
+
+ indent(out) << "oprot:writeStructBegin('" << tstruct->get_name() << "')" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ // To check element of self whether nil or not.
+ // avoid the value(false) of BOOL is lost.
+ indent(out) << "if self." << (*f_iter)->get_name() << " ~= nil then" << endl;
+ indent_up();
+ indent(out) << "oprot:writeFieldBegin('" << (*f_iter)->get_name() << "', "
+ << type_to_enum((*f_iter)->get_type()) << ", " << (*f_iter)->get_key() << ")"
+ << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "self.");
+
+ indent(out) << "oprot:writeFieldEnd()" << endl;
+ indent_down();
+ indent(out) << "end" << endl;
+ }
+ indent(out) << "oprot:writeFieldStop()" << endl;
+ indent(out) << "oprot:writeStructEnd()" << endl;
+
+ // end function
+ indent_down();
+ indent(out);
+ out << "end";
+}
+
+/**
+ * Generate a thrift service
+ */
+void t_lua_generator::generate_service(t_service* tservice) {
+ // Get output directory
+ string outdir = get_out_dir();
+
+ // Open the file for writing
+ string cur_ns = get_namespace(program_);
+ string f_service_name = outdir + cur_ns + tservice->get_name() + ".lua";
+ f_service_.open(f_service_name.c_str());
+
+ // Headers
+ f_service_ << autogen_comment() << lua_includes();
+ if (gen_requires_) {
+ f_service_ << endl << "require '" << cur_ns << "ttypes'" << endl;
+
+ if (tservice->get_extends() != NULL) {
+ f_service_ << "require '" << get_namespace(tservice->get_extends()->get_program())
+ << tservice->get_extends()->get_name() << "'" << endl;
+ }
+ }
+
+ f_service_ << endl;
+
+ generate_service_client(f_service_, tservice);
+ generate_service_interface(f_service_, tservice);
+ generate_service_processor(f_service_, tservice);
+ generate_service_helpers(f_service_, tservice);
+
+ // Close the file
+ f_service_.close();
+}
+
+void t_lua_generator::generate_service_interface(ostream& out, t_service* tservice) {
+ string classname = tservice->get_name() + "Iface";
+ t_service* extends_s = tservice->get_extends();
+
+ // Interface object definition
+ out << classname << " = ";
+ if (extends_s) {
+ out << extends_s->get_name() << "Iface:new{" << endl;
+ } else {
+ out << "__TObject:new{" << endl;
+ }
+ out << " __type = '" << classname << "'" << endl << "}" << endl << endl;
+}
+
+void t_lua_generator::generate_service_client(ostream& out, t_service* tservice) {
+ string classname = tservice->get_name() + "Client";
+ t_service* extends_s = tservice->get_extends();
+
+ // Client object definition
+ out << classname << " = __TObject.new(";
+ if (extends_s != NULL) {
+ out << extends_s->get_name() << "Client";
+ } else {
+ out << "__TClient";
+ }
+ out << ", {" << endl << " __type = '" << classname << "'" << endl << "})" << endl;
+
+ // Send/Recv functions
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string sig = function_signature(*f_iter);
+ string funcname = (*f_iter)->get_name();
+
+ // Wrapper function
+ indent(out) << endl << "function " << classname << ":" << sig << endl;
+ indent_up();
+
+ indent(out) << "self:send_" << sig << endl << indent();
+ if (!(*f_iter)->is_oneway()) {
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << "return ";
+ }
+ out << "self:recv_" << sig << endl;
+ }
+
+ indent_down();
+ indent(out) << "end" << endl;
+
+ // Send function
+ indent(out) << endl << "function " << classname << ":send_" << sig << endl;
+ indent_up();
+
+ indent(out) << "self.oprot:writeMessageBegin('" << funcname << "', "
+ << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL")
+ << ", self._seqid)" << endl;
+ indent(out) << "local args = " << funcname << "_args:new{}" << endl;
+
+ // Set the args
+ const vector<t_field*>& args = (*f_iter)->get_arglist()->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ for (fld_iter = args.begin(); fld_iter != args.end(); ++fld_iter) {
+ std::string argname = (*fld_iter)->get_name();
+ indent(out) << "args." << argname << " = " << argname << endl;
+ }
+
+ indent(out) << "args:write(self.oprot)" << endl;
+ indent(out) << "self.oprot:writeMessageEnd()" << endl;
+ indent(out) << "self.oprot.trans:flush()" << endl;
+
+ indent_down();
+ indent(out) << "end" << endl;
+
+ // Recv function
+ if (!(*f_iter)->is_oneway()) {
+ indent(out) << endl << "function " << classname << ":recv_" << sig << endl;
+ indent_up();
+
+ out << indent() << "local fname, mtype, rseqid = self.iprot:"
+ << "readMessageBegin()" << endl << indent() << "if mtype == TMessageType.EXCEPTION then"
+ << endl << indent() << " local x = TApplicationException:new{}" << endl << indent()
+ << " x:read(self.iprot)" << endl << indent() << " self.iprot:readMessageEnd()" << endl
+ << indent() << " error(x)" << endl << indent() << "end" << endl << indent()
+ << "local result = " << funcname << "_result:new{}" << endl << indent()
+ << "result:read(self.iprot)" << endl << indent() << "self.iprot:readMessageEnd()" << endl;
+
+ // Return the result if it's not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << indent() << "if result.success ~= nil then" << endl << indent() << " return result.success"
+ << endl;
+
+ // Throw custom exceptions
+ const std::vector<t_field*>& xf = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xf.begin(); x_iter != xf.end(); ++x_iter) {
+ out << indent() << "elseif result." << (*x_iter)->get_name() << " then" << endl
+ << indent() << " error(result." << (*x_iter)->get_name() << ")" << endl;
+ }
+
+ out << indent() << "end" << endl << indent()
+ << "error(TApplicationException:new{errorCode = "
+ << "TApplicationException.MISSING_RESULT})" << endl;
+ }
+
+ indent_down();
+ indent(out) << "end" << endl;
+ }
+ }
+}
+
+void t_lua_generator::generate_service_processor(ostream& out, t_service* tservice) {
+ string classname = tservice->get_name() + "Processor";
+ t_service* extends_s = tservice->get_extends();
+
+ // Define processor table
+ out << endl << classname << " = __TObject.new(";
+ if (extends_s != NULL) {
+ out << extends_s->get_name() << "Processor" << endl;
+ } else {
+ out << "__TProcessor" << endl;
+ }
+ out << ", {" << endl << " __type = '" << classname << "'" << endl << "})" << endl;
+
+ // Process function
+ indent(out) << endl << "function " << classname << ":process(iprot, oprot, server_ctx)" << endl;
+ indent_up();
+
+ indent(out) << "local name, mtype, seqid = iprot:readMessageBegin()" << endl;
+ indent(out) << "local func_name = 'process_' .. name" << endl;
+ indent(out) << "if not self[func_name] or ttype(self[func_name]) ~= 'function' then";
+ indent_up();
+ out << endl << indent() << "iprot:skip(TType.STRUCT)" << endl << indent()
+ << "iprot:readMessageEnd()" << endl << indent() << "x = TApplicationException:new{" << endl
+ << indent() << " errorCode = TApplicationException.UNKNOWN_METHOD" << endl << indent() << "}"
+ << endl << indent() << "oprot:writeMessageBegin(name, TMessageType.EXCEPTION, "
+ << "seqid)" << endl << indent() << "x:write(oprot)" << endl << indent()
+ << "oprot:writeMessageEnd()" << endl << indent() << "oprot.trans:flush()" << endl;
+ indent_down();
+ indent(out) << "else" << endl << indent()
+ << " self[func_name](self, seqid, iprot, oprot, server_ctx)" << endl << indent()
+ << "end" << endl;
+
+ indent_down();
+ indent(out) << "end" << endl;
+
+ // Generate the process subfunctions
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(out, tservice, *f_iter);
+ }
+}
+
+void t_lua_generator::generate_process_function(ostream& out,
+ t_service* tservice,
+ t_function* tfunction) {
+ string classname = tservice->get_name() + "Processor";
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+ string fn_name = tfunction->get_name();
+
+ indent(out) << endl << "function " << classname << ":process_" << fn_name
+ << "(seqid, iprot, oprot, server_ctx)" << endl;
+ indent_up();
+
+ // Read the request
+ out << indent() << "local args = " << argsname << ":new{}" << endl << indent()
+ << "local reply_type = TMessageType.REPLY" << endl << indent() << "args:read(iprot)" << endl
+ << indent() << "iprot:readMessageEnd()" << endl << indent() << "local result = " << resultname
+ << ":new{}" << endl << indent() << "local status, res = pcall(self.handler." << fn_name
+ << ", self.handler";
+
+ // Print arguments
+ t_struct* args = tfunction->get_arglist();
+ if (args->get_members().size() > 0) {
+ out << ", " << argument_list(args, "args.");
+ }
+
+ // Check for errors
+ out << ")" << endl << indent() << "if not status then" << endl << indent()
+ << " reply_type = TMessageType.EXCEPTION" << endl << indent()
+ << " result = TApplicationException:new{message = res}" << endl;
+
+ // Handle custom exceptions
+ const std::vector<t_field*>& xf = tfunction->get_xceptions()->get_members();
+ if (xf.size() > 0) {
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xf.begin(); x_iter != xf.end(); ++x_iter) {
+ out << indent() << "elseif ttype(res) == '" << (*x_iter)->get_type()->get_name() << "' then"
+ << endl << indent() << " result." << (*x_iter)->get_name() << " = res" << endl;
+ }
+ }
+
+ // Set the result and write the reply
+ out << indent() << "else" << endl << indent() << " result.success = res" << endl << indent()
+ << "end" << endl << indent() << "oprot:writeMessageBegin('" << fn_name << "', reply_type, "
+ << "seqid)" << endl << indent() << "result:write(oprot)" << endl << indent()
+ << "oprot:writeMessageEnd()" << endl << indent() << "oprot.trans:flush()" << endl;
+
+ indent_down();
+ indent(out) << "end" << endl;
+}
+
+// Service helpers
+void t_lua_generator::generate_service_helpers(ostream& out, t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ out << endl << "-- HELPER FUNCTIONS AND STRUCTURES";
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_lua_struct_definition(out, ts, false);
+ generate_function_helpers(out, *f_iter);
+ }
+}
+
+void t_lua_generator::generate_function_helpers(ostream& out, t_function* tfunction) {
+ if (!tfunction->is_oneway()) {
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+ generate_lua_struct_definition(out, &result, false);
+ }
+}
+
+/**
+ * Deserialize (Read)
+ */
+void t_lua_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ bool local,
+ string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, local, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, local, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << (local ? "local " : "") << name << " = iprot:";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "readString()";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool()";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte()";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16()";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32()";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64()";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble()";
+ break;
+ default:
+ throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32()";
+ }
+ out << endl;
+
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+void t_lua_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ bool local,
+ string prefix) {
+ indent(out) << (local ? "local " : "") << prefix << " = " << tstruct->get_name() << ":new{}"
+ << endl << indent() << prefix << ":read(iprot)" << endl;
+}
+
+void t_lua_generator::generate_deserialize_container(ostream& out,
+ t_type* ttype,
+ bool local,
+ string prefix) {
+ string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+
+ t_field fsize(g_type_i32, size);
+ t_field fktype(g_type_i8, ktype);
+ t_field fvtype(g_type_i8, vtype);
+ t_field fetype(g_type_i8, etype);
+
+ // Declare variables, read header
+ indent(out) << (local ? "local " : "") << prefix << " = {}" << endl;
+ if (ttype->is_map()) {
+ indent(out) << "local " << ktype << ", " << vtype << ", " << size << " = iprot:readMapBegin() "
+ << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "local " << etype << ", " << size << " = iprot:readSetBegin()" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "local " << etype << ", " << size << " = iprot:readListBegin()" << endl;
+ }
+
+ // Deserialize
+ indent(out) << "for _i=1," << size << " do" << endl;
+ indent_up();
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ indent_down();
+ indent(out) << "end" << endl;
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot:readMapEnd()" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot:readSetEnd()" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "iprot:readListEnd()" << endl;
+ }
+}
+
+void t_lua_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ // A map is represented by a table indexable by any lua type
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ generate_deserialize_field(out, &fkey, true);
+ generate_deserialize_field(out, &fval, true);
+
+ indent(out) << prefix << "[" << key << "] = " << val << endl;
+}
+
+void t_lua_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ // A set is represented by a table indexed by the value
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ generate_deserialize_field(out, &felem, true);
+
+ indent(out) << prefix << "[" << elem << "] = " << elem << endl;
+}
+
+void t_lua_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ // A list is represented by a table indexed by integer values
+ // LUA natively provides all of the functions required to maintain a list
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ generate_deserialize_field(out, &felem, true);
+
+ indent(out) << "table.insert(" << prefix << ", " << elem << ")" << endl;
+}
+
+/**
+ * Serialize (Write)
+ */
+void t_lua_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+ string name = prefix + tfield->get_name();
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << "oprot:";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "writeString(" << name << ")";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ")";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ")";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ")";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ")";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ")";
+ break;
+ default:
+ throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ")";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
+ name.c_str(),
+ type->get_name().c_str());
+ }
+}
+
+void t_lua_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << prefix << ":write(oprot)" << endl;
+}
+
+void t_lua_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ // Begin writing
+ if (ttype->is_map()) {
+ indent(out) << "oprot:writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "ttable_size(" << prefix << "))" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot:writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
+ << "ttable_size(" << prefix << "))" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot:writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
+ << ", "
+ << "#" << prefix << ")" << endl;
+ }
+
+ // Serialize
+ if (ttype->is_map()) {
+ string kiter = tmp("kiter");
+ string viter = tmp("viter");
+ indent(out) << "for " << kiter << "," << viter << " in pairs(" << prefix << ") do" << endl;
+ indent_up();
+ generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+ indent_down();
+ indent(out) << "end" << endl;
+ } else if (ttype->is_set()) {
+ string iter = tmp("iter");
+ indent(out) << "for " << iter << ",_ in pairs(" << prefix << ") do" << endl;
+ indent_up();
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ indent_down();
+ indent(out) << "end" << endl;
+ } else if (ttype->is_list()) {
+ string iter = tmp("iter");
+ indent(out) << "for _," << iter << " in ipairs(" << prefix << ") do" << endl;
+ indent_up();
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ indent_down();
+ indent(out) << "end" << endl;
+ }
+
+ // Finish writing
+ if (ttype->is_map()) {
+ indent(out) << "oprot:writeMapEnd()" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot:writeSetEnd()" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot:writeListEnd()" << endl;
+ }
+}
+
+void t_lua_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), kiter);
+ generate_serialize_field(out, &kfield, "");
+
+ t_field vfield(tmap->get_val_type(), viter);
+ generate_serialize_field(out, &vfield, "");
+}
+
+void t_lua_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+void t_lua_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Helper rendering functions
+ */
+string t_lua_generator::lua_includes() {
+ if (gen_requires_) {
+ return "\n\nrequire 'Thrift'";
+ } else {
+ return "";
+ }
+}
+
+string t_lua_generator::get_namespace(const t_program* program) {
+ std::string real_module = program->get_namespace("lua");
+ if (real_module.empty()) {
+ return program->get_name() + "_";
+ }
+ return real_module + "_";
+}
+
+string t_lua_generator::function_signature(t_function* tfunction, string prefix) {
+ (void)prefix;
+ std::string ret = tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")";
+ return ret;
+}
+
+string t_lua_generator::argument_list(t_struct* tstruct, string prefix) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ std::string ret = "";
+ for (fld_iter = fields.begin(); fld_iter != fields.end();) {
+ ret += prefix + (*fld_iter)->get_name();
+ ++fld_iter;
+ if (fld_iter != fields.end()) {
+ ret += ", ";
+ }
+ }
+ return ret;
+}
+
+string t_lua_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ lua,
+ "Lua",
+ " omit_requires: Suppress generation of require 'somefile'.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.cc
new file mode 100644
index 000000000..ed6a365ac
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.cc
@@ -0,0 +1,3133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+#include "thrift/generate/t_netcore_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+//TODO: check for indentation
+//TODO: Do we need seqId_ in generation?
+
+t_netcore_generator::t_netcore_generator(t_program* program, const map<string, string>& parsed_options, const string& option_string)
+ : t_oop_generator(program)
+{
+ (void)option_string;
+
+ nullable_ = false;
+ hashcode_ = false;
+ union_ = false;
+ serialize_ = false;
+ wcf_ = false;
+ wcf_namespace_.clear();
+
+ map<string, string>::const_iterator iter;
+
+ for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter)
+ {
+ if (iter->first.compare("nullable") == 0)
+ {
+ nullable_ = true;
+ }
+ else if (iter->first.compare("hashcode") == 0)
+ {
+ hashcode_ = true;
+ }
+ else if (iter->first.compare("union") == 0)
+ {
+ union_ = true;
+ }
+ else if (iter->first.compare("serial") == 0)
+ {
+ serialize_ = true;
+ wcf_namespace_ = iter->second; // since there can be only one namespace
+ }
+ else if (iter->first.compare("wcf") == 0)
+ {
+ wcf_ = true;
+ wcf_namespace_ = iter->second;
+ }
+ else
+ {
+ throw "unknown option netcore:" + iter->first;
+ }
+ }
+
+ pwarning(1, "The 'netcore' target is deprecated. Consider using 'netstd' instead.\n");
+
+ out_dir_base_ = "gen-netcore";
+}
+
+static string correct_function_name_for_async(string const& function_name)
+{
+ string const async_end = "Async";
+ size_t i = function_name.find(async_end);
+ if (i != string::npos)
+ {
+ return function_name + async_end;
+ }
+
+ return function_name;
+}
+
+/**
+* \brief Search and replace "_args" substring in struct name if exist (for C# class naming)
+* \param struct_name
+* \return Modified struct name ("Struct_args" -> "StructArgs") or original name
+*/
+static string check_and_correct_struct_name(const string& struct_name)
+{
+ string args_end = "_args";
+ size_t i = struct_name.find(args_end);
+ if (i != string::npos)
+ {
+ string new_struct_name = struct_name;
+ new_struct_name.replace(i, args_end.length(), "Args");
+ return new_struct_name;
+ }
+
+ string result_end = "_result";
+ size_t j = struct_name.find(result_end);
+ if (j != string::npos)
+ {
+ string new_struct_name = struct_name;
+ new_struct_name.replace(j, result_end.length(), "Result");
+ return new_struct_name;
+ }
+
+ return struct_name;
+}
+
+static bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; }
+
+static bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; }
+
+static bool type_can_be_null(t_type* ttype)
+{
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string();
+}
+
+bool t_netcore_generator::is_wcf_enabled() const { return wcf_; }
+
+bool t_netcore_generator::is_nullable_enabled() const { return nullable_; }
+
+bool t_netcore_generator::is_hashcode_enabled() const { return hashcode_; }
+
+bool t_netcore_generator::is_serialize_enabled() const { return serialize_; }
+
+bool t_netcore_generator::is_union_enabled() const { return union_; }
+
+map<string, int> t_netcore_generator::get_keywords_list() const
+{
+ return netcore_keywords;
+}
+
+void t_netcore_generator::init_generator()
+{
+ MKDIR(get_out_dir().c_str());
+
+ // for usage of csharp namespaces in thrift files (from files for csharp)
+ namespace_name_ = program_->get_namespace("netcore");
+ if (namespace_name_.empty())
+ {
+ namespace_name_ = program_->get_namespace("netcore");
+ }
+
+ string dir = namespace_name_;
+ string subdir = get_out_dir().c_str();
+ string::size_type loc;
+
+ while ((loc = dir.find(".")) != string::npos)
+ {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0)
+ {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ namespace_dir_ = subdir;
+ init_keywords();
+
+ while (!member_mapping_scopes.empty())
+ {
+ cleanup_member_name_mapping(member_mapping_scopes.back().scope_member);
+ }
+
+ pverbose(".NET Core options:\n");
+ pverbose("- nullable ... %s\n", (nullable_ ? "ON" : "off"));
+ pverbose("- union ...... %s\n", (union_ ? "ON" : "off"));
+ pverbose("- hashcode ... %s\n", (hashcode_ ? "ON" : "off"));
+ pverbose("- serialize .. %s\n", (serialize_ ? "ON" : "off"));
+ pverbose("- wcf ........ %s\n", (wcf_ ? "ON" : "off"));
+}
+
+string t_netcore_generator::normalize_name(string name)
+{
+ string tmp(name);
+ transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int(*)(int)>(tolower));
+
+ // un-conflict keywords by prefixing with "@"
+ if (netcore_keywords.find(tmp) != netcore_keywords.end())
+ {
+ return "@" + name;
+ }
+
+ // no changes necessary
+ return name;
+}
+
+void t_netcore_generator::init_keywords()
+{
+ netcore_keywords.clear();
+
+ // C# keywords
+ netcore_keywords["abstract"] = 1;
+ netcore_keywords["as"] = 1;
+ netcore_keywords["base"] = 1;
+ netcore_keywords["bool"] = 1;
+ netcore_keywords["break"] = 1;
+ netcore_keywords["byte"] = 1;
+ netcore_keywords["case"] = 1;
+ netcore_keywords["catch"] = 1;
+ netcore_keywords["char"] = 1;
+ netcore_keywords["checked"] = 1;
+ netcore_keywords["class"] = 1;
+ netcore_keywords["const"] = 1;
+ netcore_keywords["continue"] = 1;
+ netcore_keywords["decimal"] = 1;
+ netcore_keywords["default"] = 1;
+ netcore_keywords["delegate"] = 1;
+ netcore_keywords["do"] = 1;
+ netcore_keywords["double"] = 1;
+ netcore_keywords["else"] = 1;
+ netcore_keywords["enum"] = 1;
+ netcore_keywords["event"] = 1;
+ netcore_keywords["explicit"] = 1;
+ netcore_keywords["extern"] = 1;
+ netcore_keywords["false"] = 1;
+ netcore_keywords["finally"] = 1;
+ netcore_keywords["fixed"] = 1;
+ netcore_keywords["float"] = 1;
+ netcore_keywords["for"] = 1;
+ netcore_keywords["foreach"] = 1;
+ netcore_keywords["goto"] = 1;
+ netcore_keywords["if"] = 1;
+ netcore_keywords["implicit"] = 1;
+ netcore_keywords["in"] = 1;
+ netcore_keywords["int"] = 1;
+ netcore_keywords["interface"] = 1;
+ netcore_keywords["internal"] = 1;
+ netcore_keywords["is"] = 1;
+ netcore_keywords["lock"] = 1;
+ netcore_keywords["long"] = 1;
+ netcore_keywords["namespace"] = 1;
+ netcore_keywords["new"] = 1;
+ netcore_keywords["null"] = 1;
+ netcore_keywords["object"] = 1;
+ netcore_keywords["operator"] = 1;
+ netcore_keywords["out"] = 1;
+ netcore_keywords["override"] = 1;
+ netcore_keywords["params"] = 1;
+ netcore_keywords["private"] = 1;
+ netcore_keywords["protected"] = 1;
+ netcore_keywords["public"] = 1;
+ netcore_keywords["readonly"] = 1;
+ netcore_keywords["ref"] = 1;
+ netcore_keywords["return"] = 1;
+ netcore_keywords["sbyte"] = 1;
+ netcore_keywords["sealed"] = 1;
+ netcore_keywords["short"] = 1;
+ netcore_keywords["sizeof"] = 1;
+ netcore_keywords["stackalloc"] = 1;
+ netcore_keywords["static"] = 1;
+ netcore_keywords["string"] = 1;
+ netcore_keywords["struct"] = 1;
+ netcore_keywords["switch"] = 1;
+ netcore_keywords["this"] = 1;
+ netcore_keywords["throw"] = 1;
+ netcore_keywords["true"] = 1;
+ netcore_keywords["try"] = 1;
+ netcore_keywords["typeof"] = 1;
+ netcore_keywords["uint"] = 1;
+ netcore_keywords["ulong"] = 1;
+ netcore_keywords["unchecked"] = 1;
+ netcore_keywords["unsafe"] = 1;
+ netcore_keywords["ushort"] = 1;
+ netcore_keywords["using"] = 1;
+ netcore_keywords["virtual"] = 1;
+ netcore_keywords["void"] = 1;
+ netcore_keywords["volatile"] = 1;
+ netcore_keywords["while"] = 1;
+
+ // C# contextual keywords
+ netcore_keywords["add"] = 1;
+ netcore_keywords["alias"] = 1;
+ netcore_keywords["ascending"] = 1;
+ netcore_keywords["async"] = 1;
+ netcore_keywords["await"] = 1;
+ netcore_keywords["descending"] = 1;
+ netcore_keywords["dynamic"] = 1;
+ netcore_keywords["from"] = 1;
+ netcore_keywords["get"] = 1;
+ netcore_keywords["global"] = 1;
+ netcore_keywords["group"] = 1;
+ netcore_keywords["into"] = 1;
+ netcore_keywords["join"] = 1;
+ netcore_keywords["let"] = 1;
+ netcore_keywords["orderby"] = 1;
+ netcore_keywords["partial"] = 1;
+ netcore_keywords["remove"] = 1;
+ netcore_keywords["select"] = 1;
+ netcore_keywords["set"] = 1;
+ netcore_keywords["value"] = 1;
+ netcore_keywords["var"] = 1;
+ netcore_keywords["where"] = 1;
+ netcore_keywords["yield"] = 1;
+
+ netcore_keywords["when"] = 1;
+}
+
+void t_netcore_generator::start_netcore_namespace(ostream& out)
+{
+ if (!namespace_name_.empty())
+ {
+ out << "namespace " << namespace_name_ << endl;
+ scope_up(out);
+ }
+}
+
+void t_netcore_generator::end_netcore_namespace(ostream& out)
+{
+ if (!namespace_name_.empty())
+ {
+ scope_down(out);
+ }
+}
+
+string t_netcore_generator::netcore_type_usings() const
+{
+ string namespaces =
+ "using System;\n"
+ "using System.Collections;\n"
+ "using System.Collections.Generic;\n"
+ "using System.Text;\n"
+ "using System.IO;\n"
+ "using System.Threading;\n"
+ "using System.Threading.Tasks;\n"
+ "using Thrift;\n"
+ "using Thrift.Collections;\n";
+
+ if (wcf_)
+ {
+ namespaces += "using System.ServiceModel;\n";
+ namespaces += "using System.Runtime.Serialization;\n";
+ }
+
+ return namespaces + endl;
+}
+
+string t_netcore_generator::netcore_thrift_usings() const
+{
+ string namespaces =
+ "using Thrift.Protocols;\n"
+ "using Thrift.Protocols.Entities;\n"
+ "using Thrift.Protocols.Utilities;\n"
+ "using Thrift.Transports;\n"
+ "using Thrift.Transports.Client;\n"
+ "using Thrift.Transports.Server;\n";
+
+ return namespaces + endl;
+}
+
+void t_netcore_generator::close_generator()
+{
+}
+
+void t_netcore_generator::generate_typedef(t_typedef* ttypedef)
+{
+ (void)ttypedef;
+}
+
+void t_netcore_generator::generate_enum(t_enum* tenum)
+{
+ int ic = indent_count();
+ string f_enum_name = namespace_dir_ + "/" + tenum->get_name() + ".cs";
+
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ generate_enum(f_enum, tenum);
+
+ f_enum.close();
+ indent_validate(ic, "generate_enum");
+}
+
+void t_netcore_generator::generate_enum(ostream& out, t_enum* tenum)
+{
+ out << autogen_comment() << endl;
+
+ start_netcore_namespace(out);
+ generate_netcore_doc(out, tenum);
+
+ out << indent() << "public enum " << tenum->get_name() << endl;
+ scope_up(out);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter)
+ {
+ generate_netcore_doc(out, *c_iter);
+ int value = (*c_iter)->get_value();
+ out << indent() << (*c_iter)->get_name() << " = " << value << "," << endl;
+ }
+
+ scope_down(out);
+ end_netcore_namespace(out);
+}
+
+void t_netcore_generator::generate_consts(vector<t_const*> consts)
+{
+ if (consts.empty())
+ {
+ return;
+ }
+
+ string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ generate_consts(f_consts, consts);
+
+ f_consts.close();
+}
+
+void t_netcore_generator::generate_consts(ostream& out, vector<t_const*> consts)
+{
+ if (consts.empty())
+ {
+ return;
+ }
+
+ out << autogen_comment() << netcore_type_usings() << endl;
+
+ start_netcore_namespace(out);
+
+ out << indent() << "public static class " << make_valid_csharp_identifier(program_name_) << "Constants" << endl;
+
+ scope_up(out);
+
+ vector<t_const*>::iterator c_iter;
+ bool need_static_constructor = false;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter)
+ {
+ generate_netcore_doc(out, *c_iter);
+ if (print_const_value(out, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false))
+ {
+ need_static_constructor = true;
+ }
+ }
+
+ if (need_static_constructor)
+ {
+ print_const_constructor(out, consts);
+ }
+
+ scope_down(out);
+ end_netcore_namespace(out);
+}
+
+void t_netcore_generator::print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value)
+{
+ if (type->is_struct() || type->is_xception())
+ {
+ const vector<t_field*>& fields = static_cast<t_struct*>(type)->get_members();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ vector<t_field*>::const_iterator f_iter;
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ prepare_member_name_mapping(static_cast<t_struct*>(type));
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
+ {
+ t_field* field = NULL;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if ((*f_iter)->get_name() == v_iter->first->get_string())
+ {
+ field = *f_iter;
+ }
+ }
+
+ if (field == NULL)
+ {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ t_type* field_type = field->get_type();
+
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ out << indent() << name << "." << prop_name(field) << " = " << val << ";" << endl;
+ }
+
+ cleanup_member_name_mapping(static_cast<t_struct*>(type));
+ }
+ else if (type->is_map())
+ {
+ t_type* ktype = static_cast<t_map*>(type)->get_key_type();
+ t_type* vtype = static_cast<t_map*>(type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
+ {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ out << indent() << name << "[" << key << "]" << " = " << val << ";" << endl;
+ }
+ }
+ else if (type->is_list() || type->is_set())
+ {
+ t_type* etype;
+ if (type->is_list())
+ {
+ etype = static_cast<t_list*>(type)->get_elem_type();
+ }
+ else
+ {
+ etype = static_cast<t_set*>(type)->get_elem_type();
+ }
+
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
+ {
+ string val = render_const_value(out, name, etype, *v_iter);
+ out << indent() << name << ".Add(" << val << ");" << endl;
+ }
+ }
+}
+
+void t_netcore_generator::print_const_constructor(ostream& out, vector<t_const*> consts)
+{
+ out << indent() << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl;
+ scope_up(out);
+
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter)
+ {
+ string name = (*c_iter)->get_name();
+ t_type* type = (*c_iter)->get_type();
+ t_const_value* value = (*c_iter)->get_value();
+
+ print_const_def_value(out, name, type, value);
+ }
+ scope_down(out);
+}
+
+bool t_netcore_generator::print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype)
+{
+ out << indent();
+ bool need_static_construction = !in_static;
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ if (!defval || needtype)
+ {
+ out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ") << type_name(type) << " ";
+ }
+
+ if (type->is_base_type())
+ {
+ string v2 = render_const_value(out, name, type, value);
+ out << normalize_name(name) << " = " << v2 << ";" << endl;
+ need_static_construction = false;
+ }
+ else if (type->is_enum())
+ {
+ out << name << " = " << type_name(type, false, true) << "." << value->get_identifier_name() << ";" << endl;
+ need_static_construction = false;
+ }
+ else if (type->is_struct() || type->is_xception())
+ {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ }
+ else if (type->is_map())
+ {
+ out << name << " = new " << type_name(type, true, true) << "();" << endl;
+ }
+ else if (type->is_list() || type->is_set())
+ {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ }
+
+ if (defval && !type->is_base_type() && !type->is_enum())
+ {
+ print_const_def_value(out, name, type, value);
+ }
+
+ return need_static_construction;
+}
+
+string t_netcore_generator::render_const_value(ostream& out, string name, t_type* type, t_const_value* value)
+{
+ (void)name;
+ ostringstream render;
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER)
+ {
+ render << value->get_integer();
+ }
+ else
+ {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ else if (type->is_enum())
+ {
+ render << type->get_name() << "." << value->get_identifier_name();
+ }
+ else
+ {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true, true, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+void t_netcore_generator::generate_struct(t_struct* tstruct)
+{
+ if (union_ && tstruct->is_union())
+ {
+ generate_netcore_union(tstruct);
+ }
+ else
+ {
+ generate_netcore_struct(tstruct, false);
+ }
+}
+
+void t_netcore_generator::generate_xception(t_struct* txception)
+{
+ generate_netcore_struct(txception, true);
+}
+
+void t_netcore_generator::generate_netcore_struct(t_struct* tstruct, bool is_exception)
+{
+ int ic = indent_count();
+
+ string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_struct;
+
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << netcore_type_usings() << netcore_thrift_usings() << endl;
+
+ generate_netcore_struct_definition(f_struct, tstruct, is_exception);
+
+ f_struct.close();
+
+ indent_validate(ic, "generate_netcore_struct");
+}
+
+void t_netcore_generator::generate_netcore_struct_definition(ostream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result)
+{
+ if (!in_class)
+ {
+ start_netcore_namespace(out);
+ }
+
+ out << endl;
+
+ generate_netcore_doc(out, tstruct);
+ prepare_member_name_mapping(tstruct);
+
+ if ((serialize_ || wcf_) && !is_exception)
+ {
+ out << indent() << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+
+ bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
+
+ string sharp_struct_name = check_and_correct_struct_name(normalize_name(tstruct->get_name()));
+
+ out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << sharp_struct_name << " : ";
+
+ if (is_exception)
+ {
+ out << "TException, ";
+ }
+
+ out << "TBase" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // make private members with public Properties
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ // if the field is requied, then we use auto-properties
+ if (!field_is_required((*m_iter)) && (!nullable_ || field_has_default((*m_iter))))
+ {
+ out << indent() << "private " << declare_field(*m_iter, false, "_") << endl;
+ }
+ }
+ out << endl;
+
+ bool has_non_required_fields = false;
+ bool has_non_required_default_value_fields = false;
+ bool has_required_fields = false;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ generate_netcore_doc(out, *m_iter);
+ generate_property(out, *m_iter, true, true);
+ bool is_required = field_is_required((*m_iter));
+ bool has_default = field_has_default((*m_iter));
+ if (is_required)
+ {
+ has_required_fields = true;
+ }
+ else
+ {
+ if (has_default)
+ {
+ has_non_required_default_value_fields = true;
+ }
+ has_non_required_fields = true;
+ }
+ }
+
+ bool generate_isset = (nullable_ && has_non_required_default_value_fields) || (!nullable_ && has_non_required_fields);
+ if (generate_isset)
+ {
+ out << endl;
+ if (serialize_ || wcf_)
+ {
+ out << indent() << "[DataMember(Order = 1)]" << endl;
+ }
+ out << indent() << "public Isset __isset;" << endl;
+ if (serialize_ || wcf_)
+ {
+ out << indent() << "[DataContract]" << endl;
+ }
+
+ out << indent() << "public struct Isset" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ bool is_required = field_is_required((*m_iter));
+ bool has_default = field_has_default((*m_iter));
+ // if it is required, don't need Isset for that variable
+ // if it is not required, if it has a default value, we need to generate Isset
+ // if we are not nullable, then we generate Isset
+ if (!is_required && (!nullable_ || has_default))
+ {
+ if (serialize_ || wcf_)
+ {
+ out << indent() << "[DataMember]" << endl;
+ }
+ out << indent() << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ if (generate_isset && (serialize_ || wcf_))
+ {
+ out << indent() << "#region XmlSerializer support" << endl << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ bool is_required = field_is_required(*m_iter);
+ bool has_default = field_has_default(*m_iter);
+ // if it is required, don't need Isset for that variable
+ // if it is not required, if it has a default value, we need to generate Isset
+ // if we are not nullable, then we generate Isset
+ if (!is_required && (!nullable_ || has_default))
+ {
+ out << indent() << "public bool ShouldSerialize" << prop_name(*m_iter) << "()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+ }
+
+ out << indent() << "#endregion XmlSerializer support" << endl << endl;
+ }
+ }
+
+ // We always want a default, no argument constructor for Reading
+ out << indent() << "public " << sharp_struct_name << "()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef())
+ {
+ t = static_cast<t_typedef*>(t)->get_type();
+ }
+ if ((*m_iter)->get_value() != NULL)
+ {
+ if (field_is_required((*m_iter)))
+ {
+ print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true);
+ }
+ else
+ {
+ print_const_value(out, "this._" + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true);
+ // Optionals with defaults are marked set
+ out << indent() << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" << endl;
+ }
+ }
+ }
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ if (has_required_fields)
+ {
+ out << indent() << "public " << sharp_struct_name << "(";
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ if (field_is_required(*m_iter))
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ out << ", ";
+ }
+ out << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name();
+ }
+ }
+ out << ") : this()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ if (field_is_required(*m_iter))
+ {
+ out << indent() << "this." << prop_name(*m_iter) << " = " << (*m_iter)->get_name() << ";" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+
+ generate_netcore_struct_reader(out, tstruct);
+ if (is_result)
+ {
+ generate_netcore_struct_result_writer(out, tstruct);
+ }
+ else
+ {
+ generate_netcore_struct_writer(out, tstruct);
+ }
+ if (hashcode_)
+ {
+ generate_netcore_struct_equals(out, tstruct);
+ generate_netcore_struct_hashcode(out, tstruct);
+ }
+ generate_netcore_struct_tostring(out, tstruct);
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ // generate a corresponding WCF fault to wrap the exception
+ if ((serialize_ || wcf_) && is_exception)
+ {
+ generate_netcore_wcffault(out, tstruct);
+ }
+
+ cleanup_member_name_mapping(tstruct);
+ if (!in_class)
+ {
+ end_netcore_namespace(out);
+ }
+}
+
+void t_netcore_generator::generate_netcore_wcffault(ostream& out, t_struct* tstruct)
+{
+ out << endl;
+ out << indent() << "[DataContract]" << endl;
+
+ bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
+
+ out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << "Fault" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // make private members with public Properties
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ out << indent() << "private " << declare_field(*m_iter, false, "_") << endl;
+ }
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ generate_property(out, *m_iter, true, false);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_struct_reader(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public async Task ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "iprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Required variables aren't in __isset, so we need tmp vars to check them
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (field_is_required(*f_iter))
+ {
+ out << indent() << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
+ }
+ }
+
+ out << indent() << "TField field;" << endl
+ << indent() << "await iprot.ReadStructBeginAsync(cancellationToken);" << endl
+ << indent() << "while (true)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "field = await iprot.ReadFieldBeginAsync(cancellationToken);" << endl
+ << indent() << "if (field.Type == TType.Stop)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "break;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl
+ << indent() << "switch (field.ID)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ bool is_required = field_is_required(*f_iter);
+ out << indent() << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ out << indent() << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter);
+ if (is_required)
+ {
+ out << indent() << "isset_" << (*f_iter)->get_name() << " = true;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "else" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ out << indent() << "default: " << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "await iprot.ReadStructEndAsync(cancellationToken);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (field_is_required((*f_iter)))
+ {
+ out << indent() << "if (!isset_" << (*f_iter)->get_name() << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "iprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_struct_writer(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "var struc = new TStruct(\"" << name << "\");" << endl
+ << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
+
+ if (fields.size() > 0)
+ {
+ out << indent() << "var field = new TField();" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ bool is_required = field_is_required(*f_iter);
+ bool has_default = field_has_default(*f_iter);
+ if (nullable_ && !has_default && !is_required)
+ {
+ out << indent() << "if (" << prop_name(*f_iter) << " != null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ else if (!is_required)
+ {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed)
+ {
+ out << indent() << "if (" << prop_name(*f_iter) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ else
+ {
+ out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ }
+ out << indent() << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl
+ << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl
+ << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl
+ << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
+
+ generate_serialize_field(out, *f_iter);
+
+ out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl;
+ if (!is_required)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+ }
+
+ out << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_struct_result_writer(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "var struc = new TStruct(\"" << name << "\");" << endl
+ << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
+
+ if (fields.size() > 0)
+ {
+ out << indent() << "var field = new TField();" << endl;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ out << endl << indent() << "if";
+ }
+ else
+ {
+ out << indent() << "else if";
+ }
+
+ if (nullable_)
+ {
+ out << "(this." << prop_name((*f_iter)) << " != null)" << endl
+ << indent() << "{" << endl;
+ }
+ else
+ {
+ out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ }
+ indent_up();
+
+ bool null_allowed = !nullable_ && type_can_be_null((*f_iter)->get_type());
+ if (null_allowed)
+ {
+ out << indent() << "if (" << prop_name(*f_iter) << " != null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+
+ out << indent() << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl
+ << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl
+ << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl
+ << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
+
+ generate_serialize_field(out, *f_iter);
+
+ out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl;
+
+ if (null_allowed)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ out << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_struct_tostring(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public override string ToString()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "var sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool useFirstFlag = false;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (!field_is_required((*f_iter)))
+ {
+ out << indent() << "bool __first = true;" << endl;
+ useFirstFlag = true;
+ }
+ break;
+ }
+
+ bool had_required = false; // set to true after first required field has been processed
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ bool is_required = field_is_required((*f_iter));
+ bool has_default = field_has_default((*f_iter));
+ if (nullable_ && !has_default && !is_required)
+ {
+ out << indent() << "if (" << prop_name((*f_iter)) << " != null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ else if (!is_required)
+ {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed)
+ {
+ out << indent() << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ else
+ {
+ out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ }
+
+ if (useFirstFlag && (!had_required))
+ {
+ out << indent() << "if(!__first) { sb.Append(\", \"); }" << endl;
+ if (!is_required)
+ {
+ out << indent() << "__first = false;" << endl;
+ }
+ out << indent() << "sb.Append(\"" << prop_name(*f_iter) << ": \");" << endl;
+ }
+ else
+ {
+ out << indent() << "sb.Append(\", " << prop_name(*f_iter) << ": \");" << endl;
+ }
+
+ t_type* ttype = (*f_iter)->get_type();
+ if (ttype->is_xception() || ttype->is_struct())
+ {
+ out << indent() << "sb.Append(" << prop_name(*f_iter) << "== null ? \"<null>\" : " << prop_name(*f_iter) << ".ToString());" << endl;
+ }
+ else
+ {
+ out << indent() << "sb.Append(" << prop_name(*f_iter) << ");" << endl;
+ }
+
+ if (!is_required)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ else
+ {
+ had_required = true; // now __first must be false, so we don't need to check it anymore
+ }
+ }
+
+ out << indent() << "sb.Append(\")\");" << endl
+ << indent() << "return sb.ToString();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_netcore_generator::generate_netcore_union(t_struct* tunion)
+{
+ int ic = indent_count();
+
+ string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_union;
+
+ f_union.open(f_union_name.c_str());
+
+ f_union << autogen_comment() << netcore_type_usings() << netcore_thrift_usings() << endl;
+
+ generate_netcore_union_definition(f_union, tunion);
+
+ f_union.close();
+
+ indent_validate(ic, "generate_netcore_union.");
+}
+
+void t_netcore_generator::generate_netcore_union_definition(ostream& out, t_struct* tunion)
+{
+ // Let's define the class first
+ start_netcore_namespace(out);
+
+ out << indent() << "public abstract partial class " << tunion->get_name() << " : TAbstractBase" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "public abstract Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken);" << endl
+ << indent() << "public readonly int Isset;" << endl
+ << indent() << "public abstract object Data { get; }" << endl
+ << indent() << "protected " << tunion->get_name() << "(int isset)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "Isset = isset;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << indent() << "public class ___undefined : " << tunion->get_name() << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "public override object Data { get { return null; } }" << endl
+ << indent() << "public ___undefined() : base(0) {}" << endl << endl;
+
+ out << indent() << "public override Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist an union type which is not set.\");" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ const vector<t_field*>& fields = tunion->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ generate_netcore_union_class(out, tunion, (*f_iter));
+ }
+
+ generate_netcore_union_reader(out, tunion);
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ end_netcore_namespace(out);
+}
+
+void t_netcore_generator::generate_netcore_union_class(ostream& out, t_struct* tunion, t_field* tfield)
+{
+ out << indent() << "public " << type_name(tfield->get_type()) << " As_" << tfield->get_name() << endl;
+ out << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "get" << endl;
+ out << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return (" << tfield->get_key() << " == Isset) ? (" << type_name(tfield->get_type()) << ")Data : default(" << type_name(tfield->get_type()) << ");" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl;
+
+
+ out << indent() << "public class " << tfield->get_name() << " : " << tunion->get_name() << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "private " << type_name(tfield->get_type()) << " _data;" << endl
+ << indent() << "public override object Data { get { return _data; } }" << endl
+ << indent() << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base("<< tfield->get_key() <<")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "this._data = data;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+
+ out << indent() << "public override async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken) {" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "var struc = new TStruct(\"" << tunion->get_name() << "\");" << endl
+ << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
+
+ out << indent() << "var field = new TField();" << endl
+ << indent() << "field.Name = \"" << tfield->get_name() << "\";" << endl
+ << indent() << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl
+ << indent() << "field.ID = " << tfield->get_key() << ";" << endl
+ << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
+
+ generate_serialize_field(out, tfield, "_data", true, true);
+
+ out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_struct_equals(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public override bool Equals(object that)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "var other = that as " << check_and_correct_struct_name(normalize_name(tstruct->get_name())) << ";" << endl
+ << indent() << "if (other == null) return false;" << endl
+ << indent() << "if (ReferenceEquals(this, other)) return true;" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool first = true;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ out << indent() << "return ";
+ indent_up();
+ }
+ else
+ {
+ out << endl;
+ out << indent() << "&& ";
+ }
+ if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter))))
+ {
+ out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset."
+ << normalize_name((*f_iter)->get_name()) << ") && ((!__isset."
+ << normalize_name((*f_iter)->get_name()) << ") || (";
+ }
+ t_type* ttype = (*f_iter)->get_type();
+ if (ttype->is_container() || ttype->is_binary())
+ {
+ out << "TCollections.Equals(";
+ }
+ else
+ {
+ out << "System.Object.Equals(";
+ }
+ out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")";
+ if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter))))
+ {
+ out << ")))";
+ }
+ }
+ if (first)
+ {
+ out << indent() << "return true;" << endl;
+ }
+ else
+ {
+ out << ";" << endl;
+ indent_down();
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_struct_hashcode(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public override int GetHashCode() {" << endl;
+ indent_up();
+
+ out << indent() << "int hashcode = 0;" << endl;
+ out << indent() << "unchecked {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ t_type* ttype = (*f_iter)->get_type();
+ out << indent() << "hashcode = (hashcode * 397) ^ ";
+ if (field_is_required((*f_iter)))
+ {
+ out << "(";
+ }
+ else if (nullable_)
+ {
+ out << "(" << prop_name((*f_iter)) << " == null ? 0 : ";
+ }
+ else
+ {
+ out << "(!__isset." << normalize_name((*f_iter)->get_name()) << " ? 0 : ";
+ }
+ if (ttype->is_container())
+ {
+ out << "(TCollections.GetHashCode(" << prop_name((*f_iter)) << "))";
+ }
+ else
+ {
+ out << "(" << prop_name((*f_iter)) << ".GetHashCode())";
+ }
+ out << ");" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "return hashcode;" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_service(t_service* tservice)
+{
+ int ic = indent_count();
+
+ string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs";
+ ofstream_with_content_based_conditional_update f_service;
+ f_service.open(f_service_name.c_str());
+
+ f_service << autogen_comment() << netcore_type_usings() << netcore_thrift_usings() << endl;
+
+ start_netcore_namespace(f_service);
+
+ f_service << indent() << "public partial class " << normalize_name(service_name_) << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ generate_service_interface(f_service, tservice);
+ generate_service_client(f_service, tservice);
+ generate_service_server(f_service, tservice);
+ generate_service_helpers(f_service, tservice);
+
+ indent_down();
+ f_service << indent() << "}" << endl;
+
+ end_netcore_namespace(f_service);
+ f_service.close();
+
+ indent_validate(ic, "generate_service.");
+}
+
+void t_netcore_generator::generate_service_interface(ostream& out, t_service* tservice)
+{
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL)
+ {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " : " + extends + ".IAsync";
+ }
+
+ //out << endl << endl;
+
+ generate_netcore_doc(out, tservice);
+
+ if (wcf_)
+ {
+ out << indent() << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+
+ out << indent() << "public interface IAsync" << extends_iface << endl
+ << indent() << "{" << endl;
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ generate_netcore_doc(out, *f_iter);
+
+ // if we're using WCF, add the corresponding attributes
+ if (wcf_)
+ {
+ out << indent() << "[OperationContract]" << endl;
+
+ const vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
+ {
+ out << indent() << "[FaultContract(typeof(" + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl;
+ }
+ }
+
+ out << indent() << function_signature_async(*f_iter) << ";" << endl << endl;
+ }
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_service_helpers(ostream& out, t_service* tservice)
+{
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_netcore_struct_definition(out, ts, false, true);
+ generate_function_helpers(out, *f_iter);
+ }
+}
+
+void t_netcore_generator::generate_service_client(ostream& out, t_service* tservice)
+{
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL)
+ {
+ extends = type_name(tservice->get_extends());
+ extends_client = extends + ".Client, ";
+ }
+ else
+ {
+ extends_client = "TBaseClient, IDisposable, ";
+ }
+
+ out << endl;
+
+ generate_netcore_doc(out, tservice);
+
+ out << indent() << "public class Client : " << extends_client << "IAsync" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "public Client(TProtocol protocol) : this(protocol, protocol)" << endl
+ << indent() << "{" << endl
+ << indent() << "}" << endl
+ << endl
+ << indent() << "public Client(TProtocol inputProtocol, TProtocol outputProtocol) : base(inputProtocol, outputProtocol)"
+ << indent() << "{" << endl
+ << indent() << "}" << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator functions_iterator;
+
+ for (functions_iterator = functions.begin(); functions_iterator != functions.end(); ++functions_iterator)
+ {
+ string function_name = correct_function_name_for_async((*functions_iterator)->get_name());
+
+ // async
+ out << indent() << "public async " << function_signature_async(*functions_iterator, "") << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string argsname = (*functions_iterator)->get_name() + "Args";
+
+ out << indent() << "await OutputProtocol.WriteMessageBeginAsync(new TMessage(\"" << function_name
+ << "\", " << ((*functions_iterator)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") << ", SeqId), cancellationToken);" << endl
+ << indent() << endl
+ << indent() << "var args = new " << argsname << "();" << endl;
+
+ t_struct* arg_struct = (*functions_iterator)->get_arglist();
+ prepare_member_name_mapping(arg_struct);
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter)
+ {
+ out << indent() << "args." << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << ";" << endl;
+ }
+
+ out << indent() << endl
+ << indent() << "await args.WriteAsync(OutputProtocol, cancellationToken);" << endl
+ << indent() << "await OutputProtocol.WriteMessageEndAsync(cancellationToken);" << endl
+ << indent() << "await OutputProtocol.Transport.FlushAsync(cancellationToken);" << endl;
+
+ if (!(*functions_iterator)->is_oneway())
+ {
+ string resultname = (*functions_iterator)->get_name() + "Result";
+ t_struct noargs(program_);
+ t_struct* xs = (*functions_iterator)->get_xceptions();
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+
+ out << indent() << endl
+ << indent() << "var msg = await InputProtocol.ReadMessageBeginAsync(cancellationToken);" << endl
+ << indent() << "if (msg.Type == TMessageType.Exception)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "var x = await TApplicationException.ReadAsync(InputProtocol, cancellationToken);" << endl
+ << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl
+ << indent() << "throw x;" << endl;
+ indent_down();
+
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "var result = new " << resultname << "();" << endl
+ << indent() << "await result.ReadAsync(InputProtocol, cancellationToken);" << endl
+ << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl;
+
+ if (!(*functions_iterator)->get_returntype()->is_void())
+ {
+ if (nullable_)
+ {
+ if (type_can_be_null((*functions_iterator)->get_returntype()))
+ {
+ out << indent() << "if (result.Success != null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return result.Success;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ else
+ {
+ out << indent() << "if (result.Success.HasValue)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return result.Success.Value;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+ else
+ {
+ out << indent() << "if (result.__isset.success)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return result.Success;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
+ {
+ if (nullable_)
+ {
+ out << indent() << "if (result." << prop_name(*x_iter) << " != null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ else
+ {
+ out << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ if ((*functions_iterator)->get_returntype()->is_void())
+ {
+ out << indent() << "return;" << endl;
+ }
+ else
+ {
+ out << indent() << "throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, \""
+ << function_name << " failed: unknown result\");" << endl;
+ }
+
+ cleanup_member_name_mapping((*functions_iterator)->get_xceptions());
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+ else
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_service_server(ostream& out, t_service* tservice)
+{
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL)
+ {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".AsyncProcessor, ";
+ }
+
+ out << indent() << "public class AsyncProcessor : " << extends_processor << "ITAsyncProcessor" << endl
+ << indent() << "{" << endl;
+
+ indent_up();
+
+ out << indent() << "private IAsync _iAsync;" << endl
+ << endl
+ << indent() << "public AsyncProcessor(IAsync iAsync)";
+
+ if (!extends.empty())
+ {
+ out << " : base(iAsync)";
+ }
+
+ out << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "if (iAsync == null) throw new ArgumentNullException(nameof(iAsync));" << endl
+ << endl
+ << indent() << "_iAsync = iAsync;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ string function_name = (*f_iter)->get_name();
+ out << indent() << "processMap_[\"" << correct_function_name_for_async(function_name) << "\"] = " << function_name << "_ProcessAsync;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl
+ << endl;
+
+ if (extends.empty())
+ {
+ out << indent() << "protected delegate Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken);" << endl;
+ }
+
+ if (extends.empty())
+ {
+ out << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new Dictionary<string, ProcessFunction>();" << endl;
+ }
+
+ out << endl;
+
+ if (extends.empty())
+ {
+ out << indent() << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return await ProcessAsync(iprot, oprot, CancellationToken.None);" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << indent() << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl;
+ }
+ else
+ {
+ out << indent() << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return await ProcessAsync(iprot, oprot, CancellationToken.None);" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << indent() << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl;
+ }
+
+ out << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "var msg = await iprot.ReadMessageBeginAsync(cancellationToken);" << endl
+ << endl
+ << indent() << "ProcessFunction fn;" << endl
+ << indent() << "processMap_.TryGetValue(msg.Name, out fn);" << endl
+ << endl
+ << indent() << "if (fn == null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, TType.Struct, cancellationToken);" << endl
+ << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl
+ << indent() << "var x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + msg.Name + \"'\");" << endl
+ << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID), cancellationToken);" << endl
+ << indent() << "await x.WriteAsync(oprot, cancellationToken);" << endl
+ << indent() << "await oprot.WriteMessageEndAsync(cancellationToken);" << endl
+ << indent() << "await oprot.Transport.FlushAsync(cancellationToken);" << endl
+ << indent() << "return true;" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "await fn(msg.SeqID, iprot, oprot, cancellationToken);" << endl
+ << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "catch (IOException)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return false;" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "return true;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ generate_process_function_async(out, tservice, *f_iter);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_function_helpers(ostream& out, t_function* tfunction)
+{
+ if (tfunction->is_oneway())
+ {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void())
+ {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ result.append(*f_iter);
+ }
+
+ generate_netcore_struct_definition(out, &result, false, true, true);
+}
+
+void t_netcore_generator::generate_process_function_async(ostream& out, t_service* tservice, t_function* tfunction)
+{
+ (void)tservice;
+ out << indent() << "public async Task " << tfunction->get_name()
+ << "_ProcessAsync(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string argsname = tfunction->get_name() + "Args";
+ string resultname = tfunction->get_name() + "Result";
+
+ out << indent() << "var args = new " << argsname << "();" << endl
+ << indent() << "await args.ReadAsync(iprot, cancellationToken);" << endl
+ << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl;
+
+ if (!tfunction->is_oneway())
+ {
+ out << indent() << "var result = new " << resultname << "();" << endl;
+ }
+
+ out << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+
+ if (xceptions.size() > 0)
+ {
+ out << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void())
+ {
+ out << "result.Success = ";
+ }
+
+ out << "await _iAsync." << normalize_name(tfunction->get_name()) << "Async(";
+
+ bool first = true;
+ prepare_member_name_mapping(arg_struct);
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ out << ", ";
+ }
+
+ out << "args." << prop_name(*f_iter);
+ if (nullable_ && !type_can_be_null((*f_iter)->get_type()))
+ {
+ out << ".Value";
+ }
+ }
+
+ cleanup_member_name_mapping(arg_struct);
+
+ if (!first)
+ {
+ out << ", ";
+ }
+
+ out << "cancellationToken);" << endl;
+
+ vector<t_field*>::const_iterator x_iter;
+
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+ if (xceptions.size() > 0)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
+ {
+ out << indent() << "catch (" << type_name((*x_iter)->get_type(), false, false) << " " << (*x_iter)->get_name() << ")" << endl
+ << indent() << "{" << endl;
+
+ if (!tfunction->is_oneway())
+ {
+ indent_up();
+ out << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ }
+ out << indent() << "}" << endl;
+ }
+ }
+
+ if (!tfunction->is_oneway())
+ {
+ out << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(\""
+ << correct_function_name_for_async(tfunction->get_name()) << "\", TMessageType.Reply, seqid), cancellationToken); " << endl
+ << indent() << "await result.WriteAsync(oprot, cancellationToken);" << endl;
+ }
+ indent_down();
+
+ cleanup_member_name_mapping(xs);
+
+ out << indent() << "}" << endl
+ << indent() << "catch (TTransportException)" << endl
+ << indent() << "{" << endl
+ << indent() << " throw;" << endl
+ << indent() << "}" << endl
+ << indent() << "catch (Exception ex)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "Console.Error.WriteLine(\"Error occurred in processor:\");" << endl
+ << indent() << "Console.Error.WriteLine(ex.ToString());" << endl;
+
+ if (tfunction->is_oneway())
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ else
+ {
+ out << indent() << "var x = new TApplicationException(TApplicationException.ExceptionType.InternalError,\" Internal error.\");" << endl
+ << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(\"" << correct_function_name_for_async(tfunction->get_name())
+ << "\", TMessageType.Exception, seqid), cancellationToken);" << endl
+ << indent() << "await x.WriteAsync(oprot, cancellationToken);" << endl;
+ indent_down();
+
+ out << indent() << "}" << endl
+ << indent() << "await oprot.WriteMessageEndAsync(cancellationToken);" << endl
+ << indent() << "await oprot.Transport.FlushAsync(cancellationToken);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_netcore_union_reader(ostream& out, t_struct* tunion)
+{
+ // Thanks to THRIFT-1768, we don't need to check for required fields in the union
+ const vector<t_field*>& fields = tunion->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "public static async Task<" << tunion->get_name() << "> ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl;
+ scope_up(out);
+
+ out << indent() << "iprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ out << indent() << tunion->get_name() << " retval;" << endl;
+ out << indent() << "await iprot.ReadStructBeginAsync(cancellationToken);" << endl;
+ out << indent() << "TField field = await iprot.ReadFieldBeginAsync(cancellationToken);" << endl;
+ // we cannot have the first field be a stop -- we must have a single field defined
+ out << indent() << "if (field.Type == TType.Stop)" << endl;
+ scope_up(out);
+ out << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
+ out << indent() << "retval = new ___undefined();" << endl;
+ scope_down(out);
+ out << indent() << "else" << endl;
+ scope_up(out);
+ out << indent() << "switch (field.ID)" << endl;
+ scope_up(out);
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ out << indent() << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ out << indent() << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ out << indent() << type_name((*f_iter)->get_type()) << " temp;" << endl;
+ generate_deserialize_field(out, (*f_iter), "temp", true);
+ out << indent() << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl;
+
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);"
+ << endl << indent() << " retval = new ___undefined();" << endl << indent() << "}" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ out << indent() << "default: " << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl << indent()
+ << "retval = new ___undefined();" << endl;
+ out << indent() << "break;" << endl;
+ indent_down();
+
+ scope_down(out);
+
+ out << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
+
+ out << indent() << "if ((await iprot.ReadFieldBeginAsync(cancellationToken)).Type != TType.Stop)" << endl;
+ scope_up(out);
+ out << indent() << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
+ scope_down(out);
+
+ // end of else for TStop
+ scope_down(out);
+ out << indent() << "await iprot.ReadStructEndAsync(cancellationToken);" << endl;
+ out << indent() << "return retval;" << endl;
+ indent_down();
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "iprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netcore_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless)
+{
+ t_type* type = tfield->get_type();
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ if (type->is_void())
+ {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + (is_propertyless ? "" : prop_name(tfield));
+
+ if (type->is_struct() || type->is_xception())
+ {
+ generate_deserialize_struct(out, static_cast<t_struct*>(type), name);
+ }
+ else if (type->is_container())
+ {
+ generate_deserialize_container(out, type, name);
+ }
+ else if (type->is_base_type() || type->is_enum())
+ {
+ out << indent() << name << " = ";
+
+ if (type->is_enum())
+ {
+ out << "(" << type_name(type, false, true) << ")";
+ }
+
+ out << "await iprot.";
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary())
+ {
+ out << "ReadBinaryAsync(cancellationToken);";
+ }
+ else
+ {
+ out << "ReadStringAsync(cancellationToken);";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "ReadBoolAsync(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "ReadByteAsync(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "ReadI16Async(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "ReadI32Async(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "ReadI64Async(cancellationToken);";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "ReadDoubleAsync(cancellationToken);";
+ break;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ else if (type->is_enum())
+ {
+ out << "ReadI32Async(cancellationToken);";
+ }
+ out << endl;
+ }
+ else
+ {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str());
+ }
+}
+
+void t_netcore_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix)
+{
+ if (union_ && tstruct->is_union())
+ {
+ out << indent() << prefix << " = await " << type_name(tstruct) << ".ReadAsync(iprot, cancellationToken);" << endl;
+ }
+ else
+ {
+ out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl
+ << indent() << "await " << prefix << ".ReadAsync(iprot, cancellationToken);" << endl;
+ }
+}
+
+void t_netcore_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix)
+{
+ out << indent() << "{" << endl;
+ indent_up();
+
+ string obj;
+
+ if (ttype->is_map())
+ {
+ obj = tmp("_map");
+ }
+ else if (ttype->is_set())
+ {
+ obj = tmp("_set");
+ }
+ else if (ttype->is_list())
+ {
+ obj = tmp("_list");
+ }
+
+ out << indent() << prefix << " = new " << type_name(ttype, false, true) << "();" << endl;
+ if (ttype->is_map())
+ {
+ out << indent() << "TMap " << obj << " = await iprot.ReadMapBeginAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "TSet " << obj << " = await iprot.ReadSetBeginAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "TList " << obj << " = await iprot.ReadListBeginAsync(cancellationToken);" << endl;
+ }
+
+ string i = tmp("_i");
+ out << indent() << "for(int " << i << " = 0; " << i << " < " << obj << ".Count; ++" << i << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_map())
+ {
+ generate_deserialize_map_element(out, static_cast<t_map*>(ttype), prefix);
+ }
+ else if (ttype->is_set())
+ {
+ generate_deserialize_set_element(out, static_cast<t_set*>(ttype), prefix);
+ }
+ else if (ttype->is_list())
+ {
+ generate_deserialize_list_element(out, static_cast<t_list*>(ttype), prefix);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ if (ttype->is_map())
+ {
+ out << indent() << "await iprot.ReadMapEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "await iprot.ReadSetEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "await iprot.ReadListEndAsync(cancellationToken);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_netcore_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix)
+{
+ string key = tmp("_key");
+ string val = tmp("_val");
+
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ out << indent() << declare_field(&fkey) << endl;
+ out << indent() << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ out << indent() << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+void t_netcore_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix)
+{
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ out << indent() << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ out << indent() << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_netcore_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix)
+{
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ out << indent() << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ out << indent() << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_netcore_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix, bool is_element, bool is_propertyless)
+{
+ t_type* type = tfield->get_type();
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ string name = prefix + (is_propertyless ? "" : prop_name(tfield));
+
+ if (type->is_void())
+ {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception())
+ {
+ generate_serialize_struct(out, static_cast<t_struct*>(type), name);
+ }
+ else if (type->is_container())
+ {
+ generate_serialize_container(out, type, name);
+ }
+ else if (type->is_base_type() || type->is_enum())
+ {
+ out << indent() << "await oprot.";
+
+ string nullable_name = nullable_ && !is_element && !field_is_required(tfield) ? name + ".Value" : name;
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary())
+ {
+ out << "WriteBinaryAsync(";
+ }
+ else
+ {
+ out << "WriteStringAsync(";
+ }
+ out << name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "WriteBoolAsync(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "WriteByteAsync(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "WriteI16Async(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "WriteI32Async(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "WriteI64Async(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "WriteDoubleAsync(" << nullable_name << ", cancellationToken);";
+ break;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ else if (type->is_enum())
+ {
+ out << "WriteI32Async((int)" << nullable_name << ", cancellationToken);";
+ }
+ out << endl;
+ }
+ else
+ {
+ printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", prefix.c_str(), tfield->get_name().c_str(), type_name(type).c_str());
+ }
+}
+
+void t_netcore_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix)
+{
+ (void)tstruct;
+ out << indent() << "await " << prefix << ".WriteAsync(oprot, cancellationToken);" << endl;
+}
+
+void t_netcore_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix)
+{
+ out << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_map())
+ {
+ out << indent() << "await oprot.WriteMapBeginAsync(new TMap(" << type_to_enum(static_cast<t_map*>(ttype)->get_key_type())
+ << ", " << type_to_enum(static_cast<t_map*>(ttype)->get_val_type()) << ", " << prefix
+ << ".Count), cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "await oprot.WriteSetBeginAsync(new TSet(" << type_to_enum(static_cast<t_set*>(ttype)->get_elem_type())
+ << ", " << prefix << ".Count), cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "await oprot.WriteListBeginAsync(new TList("
+ << type_to_enum(static_cast<t_list*>(ttype)->get_elem_type()) << ", " << prefix << ".Count), cancellationToken);"
+ << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map())
+ {
+ out << indent() << "foreach (" << type_name(static_cast<t_map*>(ttype)->get_key_type()) << " " << iter
+ << " in " << prefix << ".Keys)";
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "foreach (" << type_name(static_cast<t_set*>(ttype)->get_elem_type()) << " " << iter
+ << " in " << prefix << ")";
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "foreach (" << type_name(static_cast<t_list*>(ttype)->get_elem_type()) << " " << iter
+ << " in " << prefix << ")";
+ }
+
+ out << endl;
+ out << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_map())
+ {
+ generate_serialize_map_element(out, static_cast<t_map*>(ttype), iter, prefix);
+ }
+ else if (ttype->is_set())
+ {
+ generate_serialize_set_element(out, static_cast<t_set*>(ttype), iter);
+ }
+ else if (ttype->is_list())
+ {
+ generate_serialize_list_element(out, static_cast<t_list*>(ttype), iter);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ if (ttype->is_map())
+ {
+ out << indent() << "await oprot.WriteMapEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "await oprot.WriteSetEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "await oprot.WriteListEndAsync(cancellationToken);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_netcore_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map)
+{
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "", true);
+ t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+ generate_serialize_field(out, &vfield, "", true);
+}
+
+void t_netcore_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter)
+{
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "", true);
+}
+
+void t_netcore_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter)
+{
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "", true);
+}
+
+void t_netcore_generator::generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset)
+{
+ generate_netcore_property(out, tfield, isPublic, generateIsset, "_");
+}
+
+void t_netcore_generator::generate_netcore_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset, string fieldPrefix)
+{
+ if ((serialize_ || wcf_) && isPublic)
+ {
+ out << indent() << "[DataMember(Order = 0)]" << endl;
+ }
+ bool has_default = field_has_default(tfield);
+ bool is_required = field_is_required(tfield);
+ if ((nullable_ && !has_default) || is_required)
+ {
+ out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true, is_required) << " " << prop_name(tfield) << " { get; set; }" << endl;
+ }
+ else
+ {
+ out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type(), false, false, true) << " " << prop_name(tfield) << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "get" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ bool use_nullable = false;
+ if (nullable_)
+ {
+ t_type* ttype = tfield->get_type();
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+ if (ttype->is_base_type())
+ {
+ use_nullable = static_cast<t_base_type*>(ttype)->get_base() != t_base_type::TYPE_STRING;
+ }
+ }
+
+ out << indent() << "return " << fieldPrefix + tfield->get_name() << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "set" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (use_nullable)
+ {
+ if (generateIsset)
+ {
+ out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" << endl;
+ }
+ out << indent() << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() << " = value.Value;" << endl;
+ }
+ else
+ {
+ if (generateIsset)
+ {
+ out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl;
+ }
+ out << indent() << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ out << endl;
+}
+
+string t_netcore_generator::make_valid_csharp_identifier(string const& fromName)
+{
+ string str = fromName;
+ if (str.empty())
+ {
+ return str;
+ }
+
+ // tests rely on this
+ assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+ // if the first letter is a number, we add an additional underscore in front of it
+ char c = str.at(0);
+ if (('0' <= c) && (c <= '9'))
+ {
+ str = "_" + str;
+ }
+
+ // following chars: letter, number or underscore
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ c = str.at(i);
+ if (('A' > c || c > 'Z') && ('a' > c || c > 'z') && ('0' > c || c > '9') && '_' != c)
+ {
+ str.replace(i, 1, "_");
+ }
+ }
+
+ return str;
+}
+
+void t_netcore_generator::cleanup_member_name_mapping(void* scope)
+{
+ if (member_mapping_scopes.empty())
+ {
+ throw "internal error: cleanup_member_name_mapping() no scope active";
+ }
+
+ member_mapping_scope& active = member_mapping_scopes.back();
+ if (active.scope_member != scope)
+ {
+ throw "internal error: cleanup_member_name_mapping() called for wrong struct";
+ }
+
+ member_mapping_scopes.pop_back();
+}
+
+string t_netcore_generator::get_mapped_member_name(string name)
+{
+ if (!member_mapping_scopes.empty())
+ {
+ member_mapping_scope& active = member_mapping_scopes.back();
+ map<string, string>::iterator iter = active.mapping_table.find(name);
+ if (active.mapping_table.end() != iter)
+ {
+ return iter->second;
+ }
+ }
+
+ pverbose("no mapping for member %s\n", name.c_str());
+ return name;
+}
+
+void t_netcore_generator::prepare_member_name_mapping(t_struct* tstruct)
+{
+ prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name());
+}
+
+void t_netcore_generator::prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname)
+{
+ // begin new scope
+ member_mapping_scopes.emplace_back();
+ member_mapping_scope& active = member_mapping_scopes.back();
+ active.scope_member = scope;
+
+ // current C# generator policy:
+ // - prop names are always rendered with an Uppercase first letter
+ // - struct names are used as given
+ std::set<string> used_member_names;
+ vector<t_field*>::const_iterator iter;
+
+ // prevent name conflicts with struct (CS0542 error)
+ used_member_names.insert(structname);
+
+ // prevent name conflicts with known methods (THRIFT-2942)
+ used_member_names.insert("Read");
+ used_member_names.insert("Write");
+
+ for (iter = members.begin(); iter != members.end(); ++iter)
+ {
+ string oldname = (*iter)->get_name();
+ string newname = prop_name(*iter, true);
+ while (true)
+ {
+ // new name conflicts with another member
+ if (used_member_names.find(newname) != used_member_names.end())
+ {
+ pverbose("struct %s: member %s conflicts with another member\n", structname.c_str(), newname.c_str());
+ newname += '_';
+ continue;
+ }
+
+ // add always, this helps us to detect edge cases like
+ // different spellings ("foo" and "Foo") within the same struct
+ pverbose("struct %s: member mapping %s => %s\n", structname.c_str(), oldname.c_str(), newname.c_str());
+ active.mapping_table[oldname] = newname;
+ used_member_names.insert(newname);
+ break;
+ }
+ }
+}
+
+string t_netcore_generator::prop_name(t_field* tfield, bool suppress_mapping)
+{
+ string name(tfield->get_name());
+ if (suppress_mapping)
+ {
+ name[0] = toupper(name[0]);
+ }
+ else
+ {
+ name = get_mapped_member_name(name);
+ }
+ return name;
+}
+
+string t_netcore_generator::type_name(t_type* ttype, bool in_container, bool in_init, bool in_param, bool is_required)
+{
+ (void)in_init;
+
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+
+ if (ttype->is_base_type())
+ {
+ return base_type_name(static_cast<t_base_type*>(ttype), in_container, in_param, is_required);
+ }
+
+ if (ttype->is_map())
+ {
+ t_map* tmap = static_cast<t_map*>(ttype);
+ return "Dictionary<" + type_name(tmap->get_key_type(), true) + ", " + type_name(tmap->get_val_type(), true) + ">";
+ }
+
+ if (ttype->is_set())
+ {
+ t_set* tset = static_cast<t_set*>(ttype);
+ return "THashSet<" + type_name(tset->get_elem_type(), true) + ">";
+ }
+
+ if (ttype->is_list())
+ {
+ t_list* tlist = static_cast<t_list*>(ttype);
+ return "List<" + type_name(tlist->get_elem_type(), true) + ">";
+ }
+
+ t_program* program = ttype->get_program();
+ string postfix = (!is_required && nullable_ && in_param && ttype->is_enum()) ? "?" : "";
+ if (program != NULL && program != program_)
+ {
+ string ns = program->get_namespace("netcore");
+ if (!ns.empty())
+ {
+ return ns + "." + normalize_name(ttype->get_name()) + postfix;
+ }
+ }
+
+ return normalize_name(ttype->get_name()) + postfix;
+}
+
+string t_netcore_generator::base_type_name(t_base_type* tbase, bool in_container, bool in_param, bool is_required)
+{
+ (void)in_container;
+ string postfix = (!is_required && nullable_ && in_param) ? "?" : "";
+ switch (tbase->get_base())
+ {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ {
+ if (tbase->is_binary())
+ {
+ return "byte[]";
+ }
+ return "string";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "bool" + postfix;
+ case t_base_type::TYPE_I8:
+ return "sbyte" + postfix;
+ case t_base_type::TYPE_I16:
+ return "short" + postfix;
+ case t_base_type::TYPE_I32:
+ return "int" + postfix;
+ case t_base_type::TYPE_I64:
+ return "long" + postfix;
+ case t_base_type::TYPE_DOUBLE:
+ return "double" + postfix;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base());
+ }
+}
+
+string t_netcore_generator::declare_field(t_field* tfield, bool init, string prefix)
+{
+ string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name();
+ if (init)
+ {
+ t_type* ttype = tfield->get_type();
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+ if (ttype->is_base_type() && field_has_default(tfield))
+ {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
+ }
+ else if (ttype->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(ttype)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+ }
+ else if (ttype->is_enum())
+ {
+ result += " = (" + type_name(ttype, false, true) + ")0";
+ }
+ else if (ttype->is_container())
+ {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ }
+ else
+ {
+ result += " = new " + type_name(ttype, false, true) + "()";
+ }
+ }
+ return result + ";";
+}
+
+string t_netcore_generator::function_signature(t_function* tfunction, string prefix)
+{
+ t_type* ttype = tfunction->get_returntype();
+ return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + ")";
+}
+
+string t_netcore_generator::function_signature_async(t_function* tfunction, string prefix)
+{
+ t_type* ttype = tfunction->get_returntype();
+ string task = "Task";
+ if (!ttype->is_void())
+ {
+ task += "<" + type_name(ttype) + ">";
+ }
+
+ string result = task + " " + normalize_name(prefix + tfunction->get_name()) + "Async(";
+ string args = argument_list(tfunction->get_arglist());
+ result += args;
+ if (!args.empty())
+ {
+ result += ", ";
+ }
+ result += "CancellationToken cancellationToken)";
+
+ return result;
+}
+
+string t_netcore_generator::argument_list(t_struct* tstruct)
+{
+ string result = "";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name());
+ }
+ return result;
+}
+
+string t_netcore_generator::type_to_enum(t_type* type)
+{
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.String";
+ case t_base_type::TYPE_BOOL:
+ return "TType.Bool";
+ case t_base_type::TYPE_I8:
+ return "TType.Byte";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.Double";
+ }
+ }
+ else if (type->is_enum())
+ {
+ return "TType.I32";
+ }
+ else if (type->is_struct() || type->is_xception())
+ {
+ return "TType.Struct";
+ }
+ else if (type->is_map())
+ {
+ return "TType.Map";
+ }
+ else if (type->is_set())
+ {
+ return "TType.Set";
+ }
+ else if (type->is_list())
+ {
+ return "TType.List";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+void t_netcore_generator::generate_netcore_docstring_comment(ostream& out, string contents)
+{
+ docstring_comment(out, "/// <summary>" + endl, "/// ", contents, "/// </summary>" + endl);
+}
+
+void t_netcore_generator::generate_netcore_doc(ostream& out, t_field* field)
+{
+ if (field->get_type()->is_enum())
+ {
+ string combined_message = field->get_doc() + endl + "<seealso cref=\"" + get_enum_class_name(field->get_type()) + "\"/>";
+ generate_netcore_docstring_comment(out, combined_message);
+ }
+ else
+ {
+ generate_netcore_doc(out, static_cast<t_doc*>(field));
+ }
+}
+
+void t_netcore_generator::generate_netcore_doc(ostream& out, t_doc* tdoc)
+{
+ if (tdoc->has_doc())
+ {
+ generate_netcore_docstring_comment(out, tdoc->get_doc());
+ }
+}
+
+void t_netcore_generator::generate_netcore_doc(ostream& out, t_function* tfunction)
+{
+ if (tfunction->has_doc())
+ {
+ stringstream ps;
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter)
+ {
+ t_field* p = *p_iter;
+ ps << endl << "<param name=\"" << p->get_name() << "\">";
+ if (p->has_doc())
+ {
+ string str = p->get_doc();
+ str.erase(remove(str.begin(), str.end(), '\n'), str.end());
+ ps << str;
+ }
+ ps << "</param>";
+ }
+
+ docstring_comment(out,
+ "",
+ "/// ",
+ "<summary>" + endl + tfunction->get_doc() + "</summary>" + ps.str(),
+ "");
+ }
+}
+
+void t_netcore_generator::docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end)
+{
+ if (comment_start != "")
+ {
+ out << indent() << comment_start;
+ }
+
+ stringstream docs(contents, std::ios_base::in);
+
+ while (!(docs.eof() || docs.fail()))
+ {
+ char line[1024];
+ docs.getline(line, 1024);
+
+ // Just prnt a newline when the line & prefix are empty.
+ if (strlen(line) == 0 && line_prefix == "" && !docs.eof())
+ {
+ out << endl;
+ }
+ else if (strlen(line) > 0 || !docs.eof())
+ { // skip the empty last line
+ out << indent() << line_prefix << line << endl;
+ }
+ }
+ if (comment_end != "")
+ {
+ out << indent() << comment_end;
+ }
+}
+
+string t_netcore_generator::get_enum_class_name(t_type* type)
+{
+ string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL && program != program_)
+ {
+ package = program->get_namespace("netcore") + ".";
+ }
+ return package + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ netcore,
+ "C#",
+ " wcf: Adds bindings for WCF to generated classes.\n"
+ " serial: Add serialization support to generated classes.\n"
+ " nullable: Use nullable types for properties.\n"
+ " hashcode: Generate a hashcode and equals implementation for classes.\n"
+ " union: Use new union typing, which includes a static read function for union types.\n"
+)
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.h
new file mode 100644
index 000000000..e98980a9e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netcore_generator.h
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+class t_netcore_generator : public t_oop_generator
+{
+
+ struct member_mapping_scope
+ {
+ public:
+ member_mapping_scope() : scope_member(0) { }
+ void* scope_member;
+ map<string, string> mapping_table;
+ };
+
+public:
+ t_netcore_generator(t_program* program, const map<string, string>& parsed_options, const string& option_string);
+
+ bool is_wcf_enabled() const;
+ bool is_nullable_enabled() const;
+ bool is_hashcode_enabled() const;
+ bool is_serialize_enabled() const;
+ bool is_union_enabled() const;
+ map<string, int> get_keywords_list() const;
+
+ // overrides
+ void init_generator();
+ void close_generator();
+ void generate_consts(vector<t_const*> consts);
+ void generate_consts(ostream& out, vector<t_const*> consts);
+ void generate_typedef(t_typedef* ttypedef);
+ void generate_enum(t_enum* tenum);
+ void generate_enum(ostream& out, t_enum* tenum);
+ void generate_struct(t_struct* tstruct);
+ void generate_xception(t_struct* txception);
+ void generate_service(t_service* tservice);
+
+ void generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset);
+ void generate_netcore_property(ostream& out, t_field* tfield, bool isPublic, bool includeIsset = true, string fieldPrefix = "");
+ bool print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval = false, bool needtype = false);
+ string render_const_value(ostream& out, string name, t_type* type, t_const_value* value);
+ void print_const_constructor(ostream& out, vector<t_const*> consts);
+ void print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value);
+ void generate_netcore_struct(t_struct* tstruct, bool is_exception);
+ void generate_netcore_union(t_struct* tunion);
+ void generate_netcore_struct_definition(ostream& out, t_struct* tstruct, bool is_xception = false, bool in_class = false, bool is_result = false);
+ void generate_netcore_union_definition(ostream& out, t_struct* tunion);
+ void generate_netcore_union_class(ostream& out, t_struct* tunion, t_field* tfield);
+ void generate_netcore_wcffault(ostream& out, t_struct* tstruct);
+ void generate_netcore_struct_reader(ostream& out, t_struct* tstruct);
+ void generate_netcore_struct_result_writer(ostream& out, t_struct* tstruct);
+ void generate_netcore_struct_writer(ostream& out, t_struct* tstruct);
+ void generate_netcore_struct_tostring(ostream& out, t_struct* tstruct);
+ void generate_netcore_struct_equals(ostream& out, t_struct* tstruct);
+ void generate_netcore_struct_hashcode(ostream& out, t_struct* tstruct);
+ void generate_netcore_union_reader(ostream& out, t_struct* tunion);
+ void generate_function_helpers(ostream& out, t_function* tfunction);
+ void generate_service_interface(ostream& out, t_service* tservice);
+ void generate_service_helpers(ostream& out, t_service* tservice);
+ void generate_service_client(ostream& out, t_service* tservice);
+ void generate_service_server(ostream& out, t_service* tservice);
+ void generate_process_function_async(ostream& out, t_service* tservice, t_function* function);
+ void generate_deserialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_propertyless = false);
+ void generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix = "");
+ void generate_deserialize_container(ostream& out, t_type* ttype, string prefix = "");
+ void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix = "");
+ void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix = "");
+ void generate_deserialize_list_element(ostream& out, t_list* list, string prefix = "");
+ void generate_serialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_element = false, bool is_propertyless = false);
+ void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix = "");
+ void generate_serialize_container(ostream& out, t_type* ttype, string prefix = "");
+ void generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map);
+ void generate_serialize_set_element(ostream& out, t_set* tmap, string iter);
+ void generate_serialize_list_element(ostream& out, t_list* tlist, string iter);
+ void generate_netcore_doc(ostream& out, t_field* field);
+ void generate_netcore_doc(ostream& out, t_doc* tdoc);
+ void generate_netcore_doc(ostream& out, t_function* tdoc);
+ void generate_netcore_docstring_comment(ostream& out, string contents);
+ void docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end);
+ void start_netcore_namespace(ostream& out);
+ void end_netcore_namespace(ostream& out);
+
+ string netcore_type_usings() const;
+ string netcore_thrift_usings() const;
+
+ string type_name(t_type* ttype, bool in_countainer = false, bool in_init = false, bool in_param = false, bool is_required = false);
+ string base_type_name(t_base_type* tbase, bool in_container = false, bool in_param = false, bool is_required = false);
+ string declare_field(t_field* tfield, bool init = false, string prefix = "");
+ string function_signature_async(t_function* tfunction, string prefix = "");
+ string function_signature(t_function* tfunction, string prefix = "");
+ string argument_list(t_struct* tstruct);
+ string type_to_enum(t_type* ttype);
+ string prop_name(t_field* tfield, bool suppress_mapping = false);
+ string get_enum_class_name(t_type* type);
+
+private:
+ string namespace_name_;
+ string namespace_dir_;
+
+ bool nullable_;
+ bool union_;
+ bool hashcode_;
+ bool serialize_;
+ bool wcf_;
+
+ string wcf_namespace_;
+ map<string, int> netcore_keywords;
+ vector<member_mapping_scope> member_mapping_scopes;
+
+ void init_keywords();
+ string normalize_name(string name);
+ string make_valid_csharp_identifier(string const& fromName);
+ void prepare_member_name_mapping(t_struct* tstruct);
+ void prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname);
+ void cleanup_member_name_mapping(void* scope);
+ string get_mapped_member_name(string oldname);
+};
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
new file mode 100644
index 000000000..ffe51ab0b
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
@@ -0,0 +1,3023 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+#include "thrift/generate/t_netstd_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+//TODO: check for indentation
+//TODO: Do we need seqId_ in generation?
+
+t_netstd_generator::t_netstd_generator(t_program* program, const map<string, string>& parsed_options, const string& option_string)
+ : t_oop_generator(program)
+{
+ (void)option_string;
+
+ union_ = false;
+ serialize_ = false;
+ wcf_ = false;
+ wcf_namespace_.clear();
+
+ map<string, string>::const_iterator iter;
+
+ for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter)
+ {
+ if (iter->first.compare("union") == 0)
+ {
+ union_ = true;
+ }
+ else if (iter->first.compare("serial") == 0)
+ {
+ serialize_ = true;
+ wcf_namespace_ = iter->second; // since there can be only one namespace
+ }
+ else if (iter->first.compare("wcf") == 0)
+ {
+ wcf_ = true;
+ wcf_namespace_ = iter->second;
+ }
+ else
+ {
+ throw "unknown option netstd:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-netstd";
+}
+
+static string correct_function_name_for_async(string const& function_name)
+{
+ string const async_end = "Async";
+ size_t i = function_name.find(async_end);
+ if (i != string::npos)
+ {
+ return function_name + async_end;
+ }
+
+ return function_name;
+}
+
+/**
+* \brief Search and replace "_args" substring in struct name if exist (for C# class naming)
+* \param struct_name
+* \return Modified struct name ("Struct_args" -> "StructArgs") or original name
+*/
+static string check_and_correct_struct_name(const string& struct_name)
+{
+ string args_end = "_args";
+ size_t i = struct_name.find(args_end);
+ if (i != string::npos)
+ {
+ string new_struct_name = struct_name;
+ new_struct_name.replace(i, args_end.length(), "Args");
+ return new_struct_name;
+ }
+
+ string result_end = "_result";
+ size_t j = struct_name.find(result_end);
+ if (j != string::npos)
+ {
+ string new_struct_name = struct_name;
+ new_struct_name.replace(j, result_end.length(), "Result");
+ return new_struct_name;
+ }
+
+ return struct_name;
+}
+
+static bool field_has_default(t_field* tfield) { return tfield->get_value() != NULL; }
+
+static bool field_is_required(t_field* tfield) { return tfield->get_req() == t_field::T_REQUIRED; }
+
+static bool type_can_be_null(t_type* ttype)
+{
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+
+ return ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string();
+}
+
+bool t_netstd_generator::is_wcf_enabled() const { return wcf_; }
+
+bool t_netstd_generator::is_serialize_enabled() const { return serialize_; }
+
+bool t_netstd_generator::is_union_enabled() const { return union_; }
+
+map<string, int> t_netstd_generator::get_keywords_list() const
+{
+ return netstd_keywords;
+}
+
+void t_netstd_generator::init_generator()
+{
+ MKDIR(get_out_dir().c_str());
+
+ // for usage of csharp namespaces in thrift files (from files for csharp)
+ namespace_name_ = program_->get_namespace("netstd");
+ if (namespace_name_.empty())
+ {
+ namespace_name_ = program_->get_namespace("netstd");
+ }
+
+ string dir = namespace_name_;
+ string subdir = get_out_dir().c_str();
+ string::size_type loc;
+
+ while ((loc = dir.find(".")) != string::npos)
+ {
+ subdir = subdir + "/" + dir.substr(0, loc);
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ if (dir.size() > 0)
+ {
+ subdir = subdir + "/" + dir;
+ MKDIR(subdir.c_str());
+ }
+
+ namespace_dir_ = subdir;
+ init_keywords();
+
+ while (!member_mapping_scopes.empty())
+ {
+ cleanup_member_name_mapping(member_mapping_scopes.back().scope_member);
+ }
+
+ pverbose(".NET Standard options:\n");
+ pverbose("- union ...... %s\n", (is_union_enabled() ? "ON" : "off"));
+ pverbose("- serialize .. %s\n", (is_serialize_enabled() ? "ON" : "off"));
+ pverbose("- wcf ........ %s\n", (is_wcf_enabled() ? "ON" : "off"));
+}
+
+string t_netstd_generator::normalize_name(string name)
+{
+ string tmp(name);
+ transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int(*)(int)>(tolower));
+
+ // un-conflict keywords by prefixing with "@"
+ if (netstd_keywords.find(tmp) != netstd_keywords.end())
+ {
+ return "@" + name;
+ }
+
+ // no changes necessary
+ return name;
+}
+
+void t_netstd_generator::init_keywords()
+{
+ netstd_keywords.clear();
+
+ // C# keywords
+ netstd_keywords["abstract"] = 1;
+ netstd_keywords["as"] = 1;
+ netstd_keywords["base"] = 1;
+ netstd_keywords["bool"] = 1;
+ netstd_keywords["break"] = 1;
+ netstd_keywords["byte"] = 1;
+ netstd_keywords["case"] = 1;
+ netstd_keywords["catch"] = 1;
+ netstd_keywords["char"] = 1;
+ netstd_keywords["checked"] = 1;
+ netstd_keywords["class"] = 1;
+ netstd_keywords["const"] = 1;
+ netstd_keywords["continue"] = 1;
+ netstd_keywords["decimal"] = 1;
+ netstd_keywords["default"] = 1;
+ netstd_keywords["delegate"] = 1;
+ netstd_keywords["do"] = 1;
+ netstd_keywords["double"] = 1;
+ netstd_keywords["else"] = 1;
+ netstd_keywords["enum"] = 1;
+ netstd_keywords["event"] = 1;
+ netstd_keywords["explicit"] = 1;
+ netstd_keywords["extern"] = 1;
+ netstd_keywords["false"] = 1;
+ netstd_keywords["finally"] = 1;
+ netstd_keywords["fixed"] = 1;
+ netstd_keywords["float"] = 1;
+ netstd_keywords["for"] = 1;
+ netstd_keywords["foreach"] = 1;
+ netstd_keywords["goto"] = 1;
+ netstd_keywords["if"] = 1;
+ netstd_keywords["implicit"] = 1;
+ netstd_keywords["in"] = 1;
+ netstd_keywords["int"] = 1;
+ netstd_keywords["interface"] = 1;
+ netstd_keywords["internal"] = 1;
+ netstd_keywords["is"] = 1;
+ netstd_keywords["lock"] = 1;
+ netstd_keywords["long"] = 1;
+ netstd_keywords["namespace"] = 1;
+ netstd_keywords["new"] = 1;
+ netstd_keywords["null"] = 1;
+ netstd_keywords["object"] = 1;
+ netstd_keywords["operator"] = 1;
+ netstd_keywords["out"] = 1;
+ netstd_keywords["override"] = 1;
+ netstd_keywords["params"] = 1;
+ netstd_keywords["private"] = 1;
+ netstd_keywords["protected"] = 1;
+ netstd_keywords["public"] = 1;
+ netstd_keywords["readonly"] = 1;
+ netstd_keywords["ref"] = 1;
+ netstd_keywords["return"] = 1;
+ netstd_keywords["sbyte"] = 1;
+ netstd_keywords["sealed"] = 1;
+ netstd_keywords["short"] = 1;
+ netstd_keywords["sizeof"] = 1;
+ netstd_keywords["stackalloc"] = 1;
+ netstd_keywords["static"] = 1;
+ netstd_keywords["string"] = 1;
+ netstd_keywords["struct"] = 1;
+ netstd_keywords["switch"] = 1;
+ netstd_keywords["this"] = 1;
+ netstd_keywords["throw"] = 1;
+ netstd_keywords["true"] = 1;
+ netstd_keywords["try"] = 1;
+ netstd_keywords["typeof"] = 1;
+ netstd_keywords["uint"] = 1;
+ netstd_keywords["ulong"] = 1;
+ netstd_keywords["unchecked"] = 1;
+ netstd_keywords["unsafe"] = 1;
+ netstd_keywords["ushort"] = 1;
+ netstd_keywords["using"] = 1;
+ netstd_keywords["virtual"] = 1;
+ netstd_keywords["void"] = 1;
+ netstd_keywords["volatile"] = 1;
+ netstd_keywords["while"] = 1;
+
+ // C# contextual keywords
+ netstd_keywords["add"] = 1;
+ netstd_keywords["alias"] = 1;
+ netstd_keywords["ascending"] = 1;
+ netstd_keywords["async"] = 1;
+ netstd_keywords["await"] = 1;
+ netstd_keywords["descending"] = 1;
+ netstd_keywords["dynamic"] = 1;
+ netstd_keywords["from"] = 1;
+ netstd_keywords["get"] = 1;
+ netstd_keywords["global"] = 1;
+ netstd_keywords["group"] = 1;
+ netstd_keywords["into"] = 1;
+ netstd_keywords["join"] = 1;
+ netstd_keywords["let"] = 1;
+ netstd_keywords["orderby"] = 1;
+ netstd_keywords["partial"] = 1;
+ netstd_keywords["remove"] = 1;
+ netstd_keywords["select"] = 1;
+ netstd_keywords["set"] = 1;
+ netstd_keywords["value"] = 1;
+ netstd_keywords["var"] = 1;
+ netstd_keywords["where"] = 1;
+ netstd_keywords["yield"] = 1;
+
+ netstd_keywords["when"] = 1;
+}
+
+void t_netstd_generator::start_netstd_namespace(ostream& out)
+{
+ if (!namespace_name_.empty())
+ {
+ out << "namespace " << namespace_name_ << endl;
+ scope_up(out);
+ }
+}
+
+void t_netstd_generator::end_netstd_namespace(ostream& out)
+{
+ if (!namespace_name_.empty())
+ {
+ scope_down(out);
+ }
+}
+
+string t_netstd_generator::netstd_type_usings() const
+{
+ string namespaces =
+ "using System;\n"
+ "using System.Collections;\n"
+ "using System.Collections.Generic;\n"
+ "using System.Text;\n"
+ "using System.IO;\n"
+ "using System.Threading;\n"
+ "using System.Threading.Tasks;\n"
+ "using Thrift;\n"
+ "using Thrift.Collections;\n";
+
+ if (is_wcf_enabled())
+ {
+ namespaces += "using System.ServiceModel;\n";
+ namespaces += "using System.Runtime.Serialization;\n";
+ }
+
+ return namespaces + endl;
+}
+
+string t_netstd_generator::netstd_thrift_usings() const
+{
+ string namespaces =
+ "using Thrift.Protocol;\n"
+ "using Thrift.Protocol.Entities;\n"
+ "using Thrift.Protocol.Utilities;\n"
+ "using Thrift.Transport;\n"
+ "using Thrift.Transport.Client;\n"
+ "using Thrift.Transport.Server;\n"
+ "using Thrift.Processor;\n";
+
+ return namespaces + endl;
+}
+
+void t_netstd_generator::close_generator()
+{
+}
+
+void t_netstd_generator::generate_typedef(t_typedef* ttypedef)
+{
+ (void)ttypedef;
+}
+
+void t_netstd_generator::generate_enum(t_enum* tenum)
+{
+ int ic = indent_count();
+ string f_enum_name = namespace_dir_ + "/" + tenum->get_name() + ".cs";
+
+ ofstream_with_content_based_conditional_update f_enum;
+ f_enum.open(f_enum_name.c_str());
+
+ generate_enum(f_enum, tenum);
+
+ f_enum.close();
+ indent_validate(ic, "generate_enum");
+}
+
+void t_netstd_generator::generate_enum(ostream& out, t_enum* tenum)
+{
+ out << autogen_comment() << endl;
+
+ start_netstd_namespace(out);
+ generate_netstd_doc(out, tenum);
+
+ out << indent() << "public enum " << tenum->get_name() << endl;
+ scope_up(out);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter)
+ {
+ generate_netstd_doc(out, *c_iter);
+ int value = (*c_iter)->get_value();
+ out << indent() << (*c_iter)->get_name() << " = " << value << "," << endl;
+ }
+
+ scope_down(out);
+ end_netstd_namespace(out);
+}
+
+void t_netstd_generator::generate_consts(vector<t_const*> consts)
+{
+ if (consts.empty())
+ {
+ return;
+ }
+
+ string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs";
+ ofstream_with_content_based_conditional_update f_consts;
+ f_consts.open(f_consts_name.c_str());
+
+ generate_consts(f_consts, consts);
+
+ f_consts.close();
+}
+
+void t_netstd_generator::generate_consts(ostream& out, vector<t_const*> consts)
+{
+ if (consts.empty())
+ {
+ return;
+ }
+
+ out << autogen_comment() << netstd_type_usings() << endl;
+
+ start_netstd_namespace(out);
+
+ out << indent() << "public static class " << make_valid_csharp_identifier(program_name_) << "Constants" << endl;
+
+ scope_up(out);
+
+ vector<t_const*>::iterator c_iter;
+ bool need_static_constructor = false;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter)
+ {
+ generate_netstd_doc(out, *c_iter);
+ if (print_const_value(out, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false))
+ {
+ need_static_constructor = true;
+ }
+ }
+
+ if (need_static_constructor)
+ {
+ print_const_constructor(out, consts);
+ }
+
+ scope_down(out);
+ end_netstd_namespace(out);
+}
+
+void t_netstd_generator::print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value)
+{
+ if (type->is_struct() || type->is_xception())
+ {
+ const vector<t_field*>& fields = static_cast<t_struct*>(type)->get_members();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ vector<t_field*>::const_iterator f_iter;
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ prepare_member_name_mapping(static_cast<t_struct*>(type));
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
+ {
+ t_field* field = NULL;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if ((*f_iter)->get_name() == v_iter->first->get_string())
+ {
+ field = *f_iter;
+ }
+ }
+
+ if (field == NULL)
+ {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ t_type* field_type = field->get_type();
+
+ string val = render_const_value(out, name, field_type, v_iter->second);
+ out << indent() << name << "." << prop_name(field) << " = " << val << ";" << endl;
+ }
+
+ cleanup_member_name_mapping(static_cast<t_struct*>(type));
+ }
+ else if (type->is_map())
+ {
+ t_type* ktype = static_cast<t_map*>(type)->get_key_type();
+ t_type* vtype = static_cast<t_map*>(type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
+ {
+ string key = render_const_value(out, name, ktype, v_iter->first);
+ string val = render_const_value(out, name, vtype, v_iter->second);
+ out << indent() << name << "[" << key << "]" << " = " << val << ";" << endl;
+ }
+ }
+ else if (type->is_list() || type->is_set())
+ {
+ t_type* etype;
+ if (type->is_list())
+ {
+ etype = static_cast<t_list*>(type)->get_elem_type();
+ }
+ else
+ {
+ etype = static_cast<t_set*>(type)->get_elem_type();
+ }
+
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter)
+ {
+ string val = render_const_value(out, name, etype, *v_iter);
+ out << indent() << name << ".Add(" << val << ");" << endl;
+ }
+ }
+}
+
+void t_netstd_generator::print_const_constructor(ostream& out, vector<t_const*> consts)
+{
+ out << indent() << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl;
+ scope_up(out);
+
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter)
+ {
+ string name = (*c_iter)->get_name();
+ t_type* type = (*c_iter)->get_type();
+ t_const_value* value = (*c_iter)->get_value();
+
+ print_const_def_value(out, name, type, value);
+ }
+ scope_down(out);
+}
+
+bool t_netstd_generator::print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype)
+{
+ out << indent();
+ bool need_static_construction = !in_static;
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ if (!defval || needtype)
+ {
+ out << (in_static ? "" : type->is_base_type() ? "public const " : "public static ") << type_name(type) << " ";
+ }
+
+ if (type->is_base_type())
+ {
+ string v2 = render_const_value(out, name, type, value);
+ out << normalize_name(name) << " = " << v2 << ";" << endl;
+ need_static_construction = false;
+ }
+ else if (type->is_enum())
+ {
+ out << name << " = " << type_name(type) << "." << value->get_identifier_name() << ";" << endl;
+ need_static_construction = false;
+ }
+ else if (type->is_struct() || type->is_xception())
+ {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ }
+ else if (type->is_map())
+ {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ }
+ else if (type->is_list() || type->is_set())
+ {
+ out << name << " = new " << type_name(type) << "();" << endl;
+ }
+
+ if (defval && !type->is_base_type() && !type->is_enum())
+ {
+ print_const_def_value(out, name, type, value);
+ }
+
+ return need_static_construction;
+}
+
+string t_netstd_generator::render_const_value(ostream& out, string name, t_type* type, t_const_value* value)
+{
+ (void)name;
+ ostringstream render;
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_STRING:
+ render << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ render << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ render << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER)
+ {
+ render << value->get_integer();
+ }
+ else
+ {
+ render << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ else if (type->is_enum())
+ {
+ render << type->get_name() << "." << value->get_identifier_name();
+ }
+ else
+ {
+ string t = tmp("tmp");
+ print_const_value(out, t, type, value, true, true, true);
+ render << t;
+ }
+
+ return render.str();
+}
+
+void t_netstd_generator::generate_struct(t_struct* tstruct)
+{
+ if (is_union_enabled() && tstruct->is_union())
+ {
+ generate_netstd_union(tstruct);
+ }
+ else
+ {
+ generate_netstd_struct(tstruct, false);
+ }
+}
+
+void t_netstd_generator::generate_xception(t_struct* txception)
+{
+ generate_netstd_struct(txception, true);
+}
+
+void t_netstd_generator::generate_netstd_struct(t_struct* tstruct, bool is_exception)
+{
+ int ic = indent_count();
+
+ string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_struct;
+
+ f_struct.open(f_struct_name.c_str());
+
+ f_struct << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl;
+
+ generate_netstd_struct_definition(f_struct, tstruct, is_exception);
+
+ f_struct.close();
+
+ indent_validate(ic, "generate_netstd_struct");
+}
+
+void t_netstd_generator::generate_netstd_struct_definition(ostream& out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result)
+{
+ if (!in_class)
+ {
+ start_netstd_namespace(out);
+ }
+
+ out << endl;
+
+ generate_netstd_doc(out, tstruct);
+ prepare_member_name_mapping(tstruct);
+
+ if ((is_serialize_enabled() || is_wcf_enabled()) && !is_exception)
+ {
+ out << indent() << "[DataContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+
+ bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
+
+ string sharp_struct_name = check_and_correct_struct_name(normalize_name(tstruct->get_name()));
+
+ out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << sharp_struct_name << " : ";
+
+ if (is_exception)
+ {
+ out << "TException, ";
+ }
+
+ out << "TBase" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // make private members with public Properties
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ // if the field is required, then we use auto-properties
+ if (!field_is_required((*m_iter)))
+ {
+ out << indent() << "private " << declare_field(*m_iter, false, "_") << endl;
+ }
+ }
+ out << endl;
+
+ bool has_non_required_fields = false;
+ bool has_required_fields = false;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ generate_netstd_doc(out, *m_iter);
+ generate_property(out, *m_iter, true, true);
+ bool is_required = field_is_required((*m_iter));
+ if (is_required)
+ {
+ has_required_fields = true;
+ }
+ else
+ {
+ has_non_required_fields = true;
+ }
+ }
+
+ bool generate_isset = has_non_required_fields;
+ if (generate_isset)
+ {
+ out << endl;
+ if (is_serialize_enabled() || is_wcf_enabled())
+ {
+ out << indent() << "[DataMember(Order = 1)]" << endl;
+ }
+ out << indent() << "public Isset __isset;" << endl;
+ if (is_serialize_enabled() || is_wcf_enabled())
+ {
+ out << indent() << "[DataContract]" << endl;
+ }
+
+ out << indent() << "public struct Isset" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ bool is_required = field_is_required((*m_iter));
+ // if it is required, don't need Isset for that variable
+ // if it is not required, if it has a default value, we need to generate Isset
+ if (!is_required)
+ {
+ if (is_serialize_enabled() || is_wcf_enabled())
+ {
+ out << indent() << "[DataMember]" << endl;
+ }
+ out << indent() << "public bool " << normalize_name((*m_iter)->get_name()) << ";" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ if (generate_isset && (is_serialize_enabled() || is_wcf_enabled()))
+ {
+ out << indent() << "#region XmlSerializer support" << endl << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ bool is_required = field_is_required(*m_iter);
+ // if it is required, don't need Isset for that variable
+ // if it is not required, if it has a default value, we need to generate Isset
+ if (!is_required)
+ {
+ out << indent() << "public bool ShouldSerialize" << prop_name(*m_iter) << "()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return __isset." << normalize_name((*m_iter)->get_name()) << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+ }
+
+ out << indent() << "#endregion XmlSerializer support" << endl << endl;
+ }
+ }
+
+ // We always want a default, no argument constructor for Reading
+ out << indent() << "public " << sharp_struct_name << "()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ t_type* t = (*m_iter)->get_type();
+ while (t->is_typedef())
+ {
+ t = static_cast<t_typedef*>(t)->get_type();
+ }
+ if ((*m_iter)->get_value() != NULL)
+ {
+ if (field_is_required((*m_iter)))
+ {
+ print_const_value(out, "this." + prop_name(*m_iter), t, (*m_iter)->get_value(), true, true);
+ }
+ else
+ {
+ print_const_value(out, "this._" + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true);
+ // Optionals with defaults are marked set
+ out << indent() << "this.__isset." << normalize_name((*m_iter)->get_name()) << " = true;" << endl;
+ }
+ }
+ }
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ if (has_required_fields)
+ {
+ out << indent() << "public " << sharp_struct_name << "(";
+ bool first = true;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ if (field_is_required(*m_iter))
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ out << ", ";
+ }
+ out << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name();
+ }
+ }
+ out << ") : this()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ if (field_is_required(*m_iter))
+ {
+ out << indent() << "this." << prop_name(*m_iter) << " = " << (*m_iter)->get_name() << ";" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+
+ generate_netstd_struct_reader(out, tstruct);
+ if (is_result)
+ {
+ generate_netstd_struct_result_writer(out, tstruct);
+ }
+ else
+ {
+ generate_netstd_struct_writer(out, tstruct);
+ }
+ generate_netstd_struct_equals(out, tstruct);
+ generate_netstd_struct_hashcode(out, tstruct);
+ generate_netstd_struct_tostring(out, tstruct);
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ // generate a corresponding WCF fault to wrap the exception
+ if ((is_serialize_enabled() || is_wcf_enabled()) && is_exception)
+ {
+ generate_netstd_wcffault(out, tstruct);
+ }
+
+ cleanup_member_name_mapping(tstruct);
+ if (!in_class)
+ {
+ end_netstd_namespace(out);
+ }
+}
+
+void t_netstd_generator::generate_netstd_wcffault(ostream& out, t_struct* tstruct)
+{
+ out << endl;
+ out << indent() << "[DataContract]" << endl;
+
+ bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
+
+ out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << "Fault" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // make private members with public Properties
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ // if the field is required, then we use auto-properties
+ if (!field_is_required((*m_iter)))
+ {
+ out << indent() << "private " << declare_field(*m_iter, false, "_") << endl;
+ }
+ }
+ out << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ {
+ generate_property(out, *m_iter, true, false);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_struct_reader(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public async Task ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "iprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ // Required variables aren't in __isset, so we need tmp vars to check them
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (field_is_required(*f_iter))
+ {
+ out << indent() << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl;
+ }
+ }
+
+ out << indent() << "TField field;" << endl
+ << indent() << "await iprot.ReadStructBeginAsync(cancellationToken);" << endl
+ << indent() << "while (true)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "field = await iprot.ReadFieldBeginAsync(cancellationToken);" << endl
+ << indent() << "if (field.Type == TType.Stop)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "break;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl
+ << indent() << "switch (field.ID)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ bool is_required = field_is_required(*f_iter);
+ out << indent() << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ out << indent() << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ generate_deserialize_field(out, *f_iter);
+ if (is_required)
+ {
+ out << indent() << "isset_" << (*f_iter)->get_name() << " = true;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "else" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ out << indent() << "default: " << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "await iprot.ReadStructEndAsync(cancellationToken);" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (field_is_required((*f_iter)))
+ {
+ out << indent() << "if (!isset_" << (*f_iter)->get_name() << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "iprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_struct_writer(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "var struc = new TStruct(\"" << name << "\");" << endl
+ << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
+
+ if (fields.size() > 0)
+ {
+ out << indent() << "var field = new TField();" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ bool is_required = field_is_required(*f_iter);
+ if (!is_required)
+ {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed)
+ {
+ out << indent() << "if (" << prop_name(*f_iter) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ else
+ {
+ out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ }
+ out << indent() << "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl
+ << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl
+ << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl
+ << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
+
+ generate_serialize_field(out, *f_iter);
+
+ out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl;
+ if (!is_required)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+ }
+
+ out << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_struct_result_writer(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "var struc = new TStruct(\"" << name << "\");" << endl
+ << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
+
+ if (fields.size() > 0)
+ {
+ out << indent() << "var field = new TField();" << endl;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ out << endl << indent() << "if";
+ }
+ else
+ {
+ out << indent() << "else if";
+ }
+
+ out << "(this.__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed)
+ {
+ out << indent() << "if (" << prop_name(*f_iter) << " != null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+
+ out << indent() << "field.Name = \"" << prop_name(*f_iter) << "\";" << endl
+ << indent() << "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl
+ << indent() << "field.ID = " << (*f_iter)->get_key() << ";" << endl
+ << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
+
+ generate_serialize_field(out, *f_iter);
+
+ out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl;
+
+ if (null_allowed)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ out << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_struct_tostring(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public override string ToString()" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "var sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool useFirstFlag = false;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (!field_is_required((*f_iter)))
+ {
+ out << indent() << "bool __first = true;" << endl;
+ useFirstFlag = true;
+ }
+ break;
+ }
+
+ bool had_required = false; // set to true after first required field has been processed
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ bool is_required = field_is_required((*f_iter));
+ if (!is_required)
+ {
+ bool null_allowed = type_can_be_null((*f_iter)->get_type());
+ if (null_allowed)
+ {
+ out << indent() << "if (" << prop_name((*f_iter)) << " != null && __isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ else
+ {
+ out << indent() << "if (__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+ }
+
+ if (useFirstFlag && (!had_required))
+ {
+ out << indent() << "if(!__first) { sb.Append(\", \"); }" << endl;
+ if (!is_required)
+ {
+ out << indent() << "__first = false;" << endl;
+ }
+ out << indent() << "sb.Append(\"" << prop_name(*f_iter) << ": \");" << endl;
+ }
+ else
+ {
+ out << indent() << "sb.Append(\", " << prop_name(*f_iter) << ": \");" << endl;
+ }
+
+ t_type* ttype = (*f_iter)->get_type();
+ if (ttype->is_xception() || ttype->is_struct())
+ {
+ out << indent() << "sb.Append(" << prop_name(*f_iter) << "== null ? \"<null>\" : " << prop_name(*f_iter) << ".ToString());" << endl;
+ }
+ else
+ {
+ out << indent() << "sb.Append(" << prop_name(*f_iter) << ");" << endl;
+ }
+
+ if (!is_required)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ else
+ {
+ had_required = true; // now __first must be false, so we don't need to check it anymore
+ }
+ }
+
+ out << indent() << "sb.Append(\")\");" << endl
+ << indent() << "return sb.ToString();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_netstd_generator::generate_netstd_union(t_struct* tunion)
+{
+ int ic = indent_count();
+
+ string f_union_name = namespace_dir_ + "/" + (tunion->get_name()) + ".cs";
+ ofstream_with_content_based_conditional_update f_union;
+
+ f_union.open(f_union_name.c_str());
+
+ f_union << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl;
+
+ generate_netstd_union_definition(f_union, tunion);
+
+ f_union.close();
+
+ indent_validate(ic, "generate_netstd_union.");
+}
+
+void t_netstd_generator::generate_netstd_union_definition(ostream& out, t_struct* tunion)
+{
+ // Let's define the class first
+ start_netstd_namespace(out);
+
+ out << indent() << "public abstract partial class " << tunion->get_name() << " : TUnionBase" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "public abstract Task WriteAsync(TProtocol tProtocol, CancellationToken cancellationToken);" << endl
+ << indent() << "public readonly int Isset;" << endl
+ << indent() << "public abstract object Data { get; }" << endl
+ << indent() << "protected " << tunion->get_name() << "(int isset)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "Isset = isset;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << indent() << "public class ___undefined : " << tunion->get_name() << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "public override object Data { get { return null; } }" << endl
+ << indent() << "public ___undefined() : base(0) {}" << endl << endl;
+
+ out << indent() << "public override Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw new TProtocolException( TProtocolException.INVALID_DATA, \"Cannot persist an union type which is not set.\");" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ const vector<t_field*>& fields = tunion->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ generate_netstd_union_class(out, tunion, (*f_iter));
+ }
+
+ generate_netstd_union_reader(out, tunion);
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ end_netstd_namespace(out);
+}
+
+void t_netstd_generator::generate_netstd_union_class(ostream& out, t_struct* tunion, t_field* tfield)
+{
+ out << indent() << "public " << type_name(tfield->get_type()) << " As_" << tfield->get_name() << endl;
+ out << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "get" << endl;
+ out << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return (" << tfield->get_key() << " == Isset) ? (" << type_name(tfield->get_type()) << ")Data : default(" << type_name(tfield->get_type()) << ");" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl;
+
+
+ out << indent() << "public class " << tfield->get_name() << " : " << tunion->get_name() << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "private " << type_name(tfield->get_type()) << " _data;" << endl
+ << indent() << "public override object Data { get { return _data; } }" << endl
+ << indent() << "public " << tfield->get_name() << "(" << type_name(tfield->get_type()) << " data) : base("<< tfield->get_key() <<")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "this._data = data;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+
+ out << indent() << "public override async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken) {" << endl;
+ indent_up();
+
+ out << indent() << "oprot.IncrementRecursionDepth();" << endl
+ << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "var struc = new TStruct(\"" << tunion->get_name() << "\");" << endl
+ << indent() << "await oprot.WriteStructBeginAsync(struc, cancellationToken);" << endl;
+
+ out << indent() << "var field = new TField();" << endl
+ << indent() << "field.Name = \"" << tfield->get_name() << "\";" << endl
+ << indent() << "field.Type = " << type_to_enum(tfield->get_type()) << ";" << endl
+ << indent() << "field.ID = " << tfield->get_key() << ";" << endl
+ << indent() << "await oprot.WriteFieldBeginAsync(field, cancellationToken);" << endl;
+
+ generate_serialize_field(out, tfield, "_data", true);
+
+ out << indent() << "await oprot.WriteFieldEndAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteFieldStopAsync(cancellationToken);" << endl
+ << indent() << "await oprot.WriteStructEndAsync(cancellationToken);" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "finally" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "oprot.DecrementRecursionDepth();" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_struct_equals(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public override bool Equals(object that)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "var other = that as " << check_and_correct_struct_name(normalize_name(tstruct->get_name())) << ";" << endl
+ << indent() << "if (other == null) return false;" << endl
+ << indent() << "if (ReferenceEquals(this, other)) return true;" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ bool first = true;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ out << indent() << "return ";
+ indent_up();
+ }
+ else
+ {
+ out << endl;
+ out << indent() << "&& ";
+ }
+ if (!field_is_required((*f_iter)))
+ {
+ out << "((__isset." << normalize_name((*f_iter)->get_name()) << " == other.__isset."
+ << normalize_name((*f_iter)->get_name()) << ") && ((!__isset."
+ << normalize_name((*f_iter)->get_name()) << ") || (";
+ }
+ t_type* ttype = (*f_iter)->get_type();
+ if (ttype->is_container() || ttype->is_binary())
+ {
+ out << "TCollections.Equals(";
+ }
+ else
+ {
+ out << "System.Object.Equals(";
+ }
+ out << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")";
+ if (!field_is_required((*f_iter)))
+ {
+ out << ")))";
+ }
+ }
+ if (first)
+ {
+ out << indent() << "return true;" << endl;
+ }
+ else
+ {
+ out << ";" << endl;
+ indent_down();
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_struct_hashcode(ostream& out, t_struct* tstruct)
+{
+ out << indent() << "public override int GetHashCode() {" << endl;
+ indent_up();
+
+ out << indent() << "int hashcode = 157;" << endl;
+ out << indent() << "unchecked {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ t_type* ttype = (*f_iter)->get_type();
+ if (!field_is_required((*f_iter)))
+ {
+ out << indent() << "if(__isset." << normalize_name((*f_iter)->get_name()) << ")" << endl;
+ indent_up();
+ }
+ out << indent() << "hashcode = (hashcode * 397) + ";
+ if (ttype->is_container())
+ {
+ out << "TCollections.GetHashCode(" << prop_name((*f_iter)) << ")";
+ }
+ else
+ {
+ out << prop_name((*f_iter)) << ".GetHashCode()";
+ }
+ out << ";" << endl;
+
+ if (!field_is_required((*f_iter)))
+ {
+ indent_down();
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "return hashcode;" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_service(t_service* tservice)
+{
+ int ic = indent_count();
+
+ string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs";
+ ofstream_with_content_based_conditional_update f_service;
+ f_service.open(f_service_name.c_str());
+
+ f_service << autogen_comment() << netstd_type_usings() << netstd_thrift_usings() << endl;
+
+ start_netstd_namespace(f_service);
+
+ f_service << indent() << "public partial class " << normalize_name(service_name_) << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ generate_service_interface(f_service, tservice);
+ generate_service_client(f_service, tservice);
+ generate_service_server(f_service, tservice);
+ generate_service_helpers(f_service, tservice);
+
+ indent_down();
+ f_service << indent() << "}" << endl;
+
+ end_netstd_namespace(f_service);
+ f_service.close();
+
+ indent_validate(ic, "generate_service.");
+}
+
+void t_netstd_generator::generate_service_interface(ostream& out, t_service* tservice)
+{
+ string extends = "";
+ string extends_iface = "";
+ if (tservice->get_extends() != NULL)
+ {
+ extends = type_name(tservice->get_extends());
+ extends_iface = " : " + extends + ".IAsync";
+ }
+
+ //out << endl << endl;
+
+ generate_netstd_doc(out, tservice);
+
+ if (is_wcf_enabled())
+ {
+ out << indent() << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl;
+ }
+
+ out << indent() << "public interface IAsync" << extends_iface << endl
+ << indent() << "{" << endl;
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ generate_netstd_doc(out, *f_iter);
+
+ // if we're using WCF, add the corresponding attributes
+ if (is_wcf_enabled())
+ {
+ out << indent() << "[OperationContract]" << endl;
+
+ const vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
+ {
+ out << indent() << "[FaultContract(typeof(" + type_name((*x_iter)->get_type()) + "Fault))]" << endl;
+ }
+ }
+
+ out << indent() << function_signature_async(*f_iter) << ";" << endl << endl;
+ }
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_service_helpers(ostream& out, t_service* tservice)
+{
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_netstd_struct_definition(out, ts, false, true);
+ generate_function_helpers(out, *f_iter);
+ }
+}
+
+void t_netstd_generator::generate_service_client(ostream& out, t_service* tservice)
+{
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL)
+ {
+ extends = type_name(tservice->get_extends());
+ extends_client = extends + ".Client, ";
+ }
+ else
+ {
+ extends_client = "TBaseClient, IDisposable, ";
+ }
+
+ out << endl;
+
+ generate_netstd_doc(out, tservice);
+
+ out << indent() << "public class Client : " << extends_client << "IAsync" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "public Client(TProtocol protocol) : this(protocol, protocol)" << endl
+ << indent() << "{" << endl
+ << indent() << "}" << endl
+ << endl
+ << indent() << "public Client(TProtocol inputProtocol, TProtocol outputProtocol) : base(inputProtocol, outputProtocol)"
+ << indent() << "{" << endl
+ << indent() << "}" << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator functions_iterator;
+
+ for (functions_iterator = functions.begin(); functions_iterator != functions.end(); ++functions_iterator)
+ {
+ string function_name = correct_function_name_for_async((*functions_iterator)->get_name());
+
+ // async
+ out << indent() << "public async " << function_signature_async(*functions_iterator, "") << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string argsname = (*functions_iterator)->get_name() + "Args";
+
+ out << indent() << "await OutputProtocol.WriteMessageBeginAsync(new TMessage(\"" << function_name
+ << "\", " << ((*functions_iterator)->is_oneway() ? "TMessageType.Oneway" : "TMessageType.Call") << ", SeqId), cancellationToken);" << endl
+ << indent() << endl
+ << indent() << "var args = new " << argsname << "();" << endl;
+
+ t_struct* arg_struct = (*functions_iterator)->get_arglist();
+ prepare_member_name_mapping(arg_struct);
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter)
+ {
+ out << indent() << "args." << prop_name(*fld_iter) << " = " << normalize_name((*fld_iter)->get_name()) << ";" << endl;
+ }
+
+ out << indent() << endl
+ << indent() << "await args.WriteAsync(OutputProtocol, cancellationToken);" << endl
+ << indent() << "await OutputProtocol.WriteMessageEndAsync(cancellationToken);" << endl
+ << indent() << "await OutputProtocol.Transport.FlushAsync(cancellationToken);" << endl;
+
+ if (!(*functions_iterator)->is_oneway())
+ {
+ string resultname = (*functions_iterator)->get_name() + "Result";
+ t_struct noargs(program_);
+ t_struct* xs = (*functions_iterator)->get_xceptions();
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+
+ out << indent() << endl
+ << indent() << "var msg = await InputProtocol.ReadMessageBeginAsync(cancellationToken);" << endl
+ << indent() << "if (msg.Type == TMessageType.Exception)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "var x = await TApplicationException.ReadAsync(InputProtocol, cancellationToken);" << endl
+ << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl
+ << indent() << "throw x;" << endl;
+ indent_down();
+
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "var result = new " << resultname << "();" << endl
+ << indent() << "await result.ReadAsync(InputProtocol, cancellationToken);" << endl
+ << indent() << "await InputProtocol.ReadMessageEndAsync(cancellationToken);" << endl;
+
+ if (!(*functions_iterator)->get_returntype()->is_void())
+ {
+ out << indent() << "if (result.__isset.success)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return result.Success;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
+ {
+ out << indent() << "if (result.__isset." << normalize_name((*x_iter)->get_name()) << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "throw result." << prop_name(*x_iter) << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ if ((*functions_iterator)->get_returntype()->is_void())
+ {
+ out << indent() << "return;" << endl;
+ }
+ else
+ {
+ out << indent() << "throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, \""
+ << function_name << " failed: unknown result\");" << endl;
+ }
+
+ cleanup_member_name_mapping((*functions_iterator)->get_xceptions());
+ indent_down();
+ out << indent() << "}" << endl << endl;
+ }
+ else
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_service_server(ostream& out, t_service* tservice)
+{
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL)
+ {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".AsyncProcessor, ";
+ }
+
+ out << indent() << "public class AsyncProcessor : " << extends_processor << "ITAsyncProcessor" << endl
+ << indent() << "{" << endl;
+
+ indent_up();
+
+ out << indent() << "private IAsync _iAsync;" << endl
+ << endl
+ << indent() << "public AsyncProcessor(IAsync iAsync)";
+
+ if (!extends.empty())
+ {
+ out << " : base(iAsync)";
+ }
+
+ out << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "if (iAsync == null) throw new ArgumentNullException(nameof(iAsync));" << endl
+ << endl
+ << indent() << "_iAsync = iAsync;" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ string function_name = (*f_iter)->get_name();
+ out << indent() << "processMap_[\"" << correct_function_name_for_async(function_name) << "\"] = " << function_name << "_ProcessAsync;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl
+ << endl;
+
+ if (extends.empty())
+ {
+ out << indent() << "protected delegate Task ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken);" << endl;
+ }
+
+ if (extends.empty())
+ {
+ out << indent() << "protected Dictionary<string, ProcessFunction> processMap_ = new Dictionary<string, ProcessFunction>();" << endl;
+ }
+
+ out << endl;
+
+ if (extends.empty())
+ {
+ out << indent() << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return await ProcessAsync(iprot, oprot, CancellationToken.None);" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << indent() << "public async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl;
+ }
+ else
+ {
+ out << indent() << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return await ProcessAsync(iprot, oprot, CancellationToken.None);" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << indent() << "public new async Task<bool> ProcessAsync(TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl;
+ }
+
+ out << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "var msg = await iprot.ReadMessageBeginAsync(cancellationToken);" << endl
+ << endl
+ << indent() << "ProcessFunction fn;" << endl
+ << indent() << "processMap_.TryGetValue(msg.Name, out fn);" << endl
+ << endl
+ << indent() << "if (fn == null)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, TType.Struct, cancellationToken);" << endl
+ << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl
+ << indent() << "var x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + msg.Name + \"'\");" << endl
+ << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID), cancellationToken);" << endl
+ << indent() << "await x.WriteAsync(oprot, cancellationToken);" << endl
+ << indent() << "await oprot.WriteMessageEndAsync(cancellationToken);" << endl
+ << indent() << "await oprot.Transport.FlushAsync(cancellationToken);" << endl
+ << indent() << "return true;" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "await fn(msg.SeqID, iprot, oprot, cancellationToken);" << endl
+ << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ out << indent() << "catch (IOException)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ out << indent() << "return false;" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << endl
+ << indent() << "return true;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
+ {
+ generate_process_function_async(out, tservice, *f_iter);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_function_helpers(ostream& out, t_function* tfunction)
+{
+ if (tfunction->is_oneway())
+ {
+ return;
+ }
+
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void())
+ {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ result.append(*f_iter);
+ }
+
+ generate_netstd_struct_definition(out, &result, false, true, true);
+}
+
+void t_netstd_generator::generate_process_function_async(ostream& out, t_service* tservice, t_function* tfunction)
+{
+ (void)tservice;
+ out << indent() << "public async Task " << tfunction->get_name()
+ << "_ProcessAsync(int seqid, TProtocol iprot, TProtocol oprot, CancellationToken cancellationToken)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string argsname = tfunction->get_name() + "Args";
+ string resultname = tfunction->get_name() + "Result";
+
+ out << indent() << "var args = new " << argsname << "();" << endl
+ << indent() << "await args.ReadAsync(iprot, cancellationToken);" << endl
+ << indent() << "await iprot.ReadMessageEndAsync(cancellationToken);" << endl;
+
+ if (!tfunction->is_oneway())
+ {
+ out << indent() << "var result = new " << resultname << "();" << endl;
+ }
+
+ out << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+
+ if (xceptions.size() > 0)
+ {
+ out << indent() << "try" << endl
+ << indent() << "{" << endl;
+ indent_up();
+ }
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void())
+ {
+ out << "result.Success = ";
+ }
+
+ out << "await _iAsync." << normalize_name(tfunction->get_name()) << "Async(";
+
+ bool first = true;
+ prepare_member_name_mapping(arg_struct);
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ out << ", ";
+ }
+
+ out << "args." << prop_name(*f_iter);
+ }
+
+ cleanup_member_name_mapping(arg_struct);
+
+ if (!first)
+ {
+ out << ", ";
+ }
+
+ out << "cancellationToken);" << endl;
+
+ vector<t_field*>::const_iterator x_iter;
+
+ prepare_member_name_mapping(xs, xs->get_members(), resultname);
+ if (xceptions.size() > 0)
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter)
+ {
+ out << indent() << "catch (" << type_name((*x_iter)->get_type()) << " " << (*x_iter)->get_name() << ")" << endl
+ << indent() << "{" << endl;
+
+ if (!tfunction->is_oneway())
+ {
+ indent_up();
+ out << indent() << "result." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ }
+ out << indent() << "}" << endl;
+ }
+ }
+
+ if (!tfunction->is_oneway())
+ {
+ out << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(\""
+ << correct_function_name_for_async(tfunction->get_name()) << "\", TMessageType.Reply, seqid), cancellationToken); " << endl
+ << indent() << "await result.WriteAsync(oprot, cancellationToken);" << endl;
+ }
+ indent_down();
+
+ cleanup_member_name_mapping(xs);
+
+ out << indent() << "}" << endl
+ << indent() << "catch (TTransportException)" << endl
+ << indent() << "{" << endl
+ << indent() << " throw;" << endl
+ << indent() << "}" << endl
+ << indent() << "catch (Exception ex)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "Console.Error.WriteLine(\"Error occurred in processor:\");" << endl
+ << indent() << "Console.Error.WriteLine(ex.ToString());" << endl;
+
+ if (tfunction->is_oneway())
+ {
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ else
+ {
+ out << indent() << "var x = new TApplicationException(TApplicationException.ExceptionType.InternalError,\" Internal error.\");" << endl
+ << indent() << "await oprot.WriteMessageBeginAsync(new TMessage(\"" << correct_function_name_for_async(tfunction->get_name())
+ << "\", TMessageType.Exception, seqid), cancellationToken);" << endl
+ << indent() << "await x.WriteAsync(oprot, cancellationToken);" << endl;
+ indent_down();
+
+ out << indent() << "}" << endl
+ << indent() << "await oprot.WriteMessageEndAsync(cancellationToken);" << endl
+ << indent() << "await oprot.Transport.FlushAsync(cancellationToken);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_netstd_union_reader(ostream& out, t_struct* tunion)
+{
+ // Thanks to THRIFT-1768, we don't need to check for required fields in the union
+ const vector<t_field*>& fields = tunion->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent() << "public static async Task<" << tunion->get_name() << "> ReadAsync(TProtocol iprot, CancellationToken cancellationToken)" << endl;
+ scope_up(out);
+
+ out << indent() << "iprot.IncrementRecursionDepth();" << endl;
+ out << indent() << "try" << endl;
+ scope_up(out);
+
+ out << indent() << tunion->get_name() << " retval;" << endl;
+ out << indent() << "await iprot.ReadStructBeginAsync(cancellationToken);" << endl;
+ out << indent() << "TField field = await iprot.ReadFieldBeginAsync(cancellationToken);" << endl;
+ // we cannot have the first field be a stop -- we must have a single field defined
+ out << indent() << "if (field.Type == TType.Stop)" << endl;
+ scope_up(out);
+ out << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
+ out << indent() << "retval = new ___undefined();" << endl;
+ scope_down(out);
+ out << indent() << "else" << endl;
+ scope_up(out);
+ out << indent() << "switch (field.ID)" << endl;
+ scope_up(out);
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ out << indent() << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ out << indent() << "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+
+ out << indent() << type_name((*f_iter)->get_type()) << " temp;" << endl;
+ generate_deserialize_field(out, (*f_iter), "temp", true);
+ out << indent() << "retval = new " << (*f_iter)->get_name() << "(temp);" << endl;
+
+ indent_down();
+ out << indent() << "} else { " << endl << indent() << " await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);"
+ << endl << indent() << " retval = new ___undefined();" << endl << indent() << "}" << endl
+ << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ out << indent() << "default: " << endl;
+ indent_up();
+ out << indent() << "await TProtocolUtil.SkipAsync(iprot, field.Type, cancellationToken);" << endl << indent()
+ << "retval = new ___undefined();" << endl;
+ out << indent() << "break;" << endl;
+ indent_down();
+
+ scope_down(out);
+
+ out << indent() << "await iprot.ReadFieldEndAsync(cancellationToken);" << endl;
+
+ out << indent() << "if ((await iprot.ReadFieldBeginAsync(cancellationToken)).Type != TType.Stop)" << endl;
+ scope_up(out);
+ out << indent() << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl;
+ scope_down(out);
+
+ // end of else for TStop
+ scope_down(out);
+ out << indent() << "await iprot.ReadStructEndAsync(cancellationToken);" << endl;
+ out << indent() << "return retval;" << endl;
+ indent_down();
+
+ scope_down(out);
+ out << indent() << "finally" << endl;
+ scope_up(out);
+ out << indent() << "iprot.DecrementRecursionDepth();" << endl;
+ scope_down(out);
+
+ out << indent() << "}" << endl << endl;
+}
+
+void t_netstd_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless)
+{
+ t_type* type = tfield->get_type();
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ if (type->is_void())
+ {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + (is_propertyless ? "" : prop_name(tfield));
+
+ if (type->is_struct() || type->is_xception())
+ {
+ generate_deserialize_struct(out, static_cast<t_struct*>(type), name);
+ }
+ else if (type->is_container())
+ {
+ generate_deserialize_container(out, type, name);
+ }
+ else if (type->is_base_type() || type->is_enum())
+ {
+ out << indent() << name << " = ";
+
+ if (type->is_enum())
+ {
+ out << "(" << type_name(type) << ")";
+ }
+
+ out << "await iprot.";
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary())
+ {
+ out << "ReadBinaryAsync(cancellationToken);";
+ }
+ else
+ {
+ out << "ReadStringAsync(cancellationToken);";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "ReadBoolAsync(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "ReadByteAsync(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "ReadI16Async(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "ReadI32Async(cancellationToken);";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "ReadI64Async(cancellationToken);";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "ReadDoubleAsync(cancellationToken);";
+ break;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ else if (type->is_enum())
+ {
+ out << "ReadI32Async(cancellationToken);";
+ }
+ out << endl;
+ }
+ else
+ {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str());
+ }
+}
+
+void t_netstd_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix)
+{
+ if (is_union_enabled() && tstruct->is_union())
+ {
+ out << indent() << prefix << " = await " << type_name(tstruct) << ".ReadAsync(iprot, cancellationToken);" << endl;
+ }
+ else
+ {
+ out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl
+ << indent() << "await " << prefix << ".ReadAsync(iprot, cancellationToken);" << endl;
+ }
+}
+
+void t_netstd_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix)
+{
+ out << indent() << "{" << endl;
+ indent_up();
+
+ string obj;
+
+ if (ttype->is_map())
+ {
+ obj = tmp("_map");
+ }
+ else if (ttype->is_set())
+ {
+ obj = tmp("_set");
+ }
+ else if (ttype->is_list())
+ {
+ obj = tmp("_list");
+ }
+
+ if (ttype->is_map())
+ {
+ out << indent() << "TMap " << obj << " = await iprot.ReadMapBeginAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "TSet " << obj << " = await iprot.ReadSetBeginAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "TList " << obj << " = await iprot.ReadListBeginAsync(cancellationToken);" << endl;
+ }
+
+ out << indent() << prefix << " = new " << type_name(ttype) << "(" << obj << ".Count);" << endl;
+ string i = tmp("_i");
+ out << indent() << "for(int " << i << " = 0; " << i << " < " << obj << ".Count; ++" << i << ")" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_map())
+ {
+ generate_deserialize_map_element(out, static_cast<t_map*>(ttype), prefix);
+ }
+ else if (ttype->is_set())
+ {
+ generate_deserialize_set_element(out, static_cast<t_set*>(ttype), prefix);
+ }
+ else if (ttype->is_list())
+ {
+ generate_deserialize_list_element(out, static_cast<t_list*>(ttype), prefix);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ if (ttype->is_map())
+ {
+ out << indent() << "await iprot.ReadMapEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "await iprot.ReadSetEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "await iprot.ReadListEndAsync(cancellationToken);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_netstd_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix)
+{
+ string key = tmp("_key");
+ string val = tmp("_val");
+
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ out << indent() << declare_field(&fkey) << endl;
+ out << indent() << declare_field(&fval) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ out << indent() << prefix << "[" << key << "] = " << val << ";" << endl;
+}
+
+void t_netstd_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix)
+{
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ out << indent() << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ out << indent() << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_netstd_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix)
+{
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ out << indent() << declare_field(&felem) << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ out << indent() << prefix << ".Add(" << elem << ");" << endl;
+}
+
+void t_netstd_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix, bool is_propertyless)
+{
+ t_type* type = tfield->get_type();
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ string name = prefix + (is_propertyless ? "" : prop_name(tfield));
+
+ if (type->is_void())
+ {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
+ }
+
+ if (type->is_struct() || type->is_xception())
+ {
+ generate_serialize_struct(out, static_cast<t_struct*>(type), name);
+ }
+ else if (type->is_container())
+ {
+ generate_serialize_container(out, type, name);
+ }
+ else if (type->is_base_type() || type->is_enum())
+ {
+ out << indent() << "await oprot.";
+
+ string nullable_name = name;
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary())
+ {
+ out << "WriteBinaryAsync(";
+ }
+ else
+ {
+ out << "WriteStringAsync(";
+ }
+ out << name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "WriteBoolAsync(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "WriteByteAsync(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "WriteI16Async(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "WriteI32Async(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "WriteI64Async(" << nullable_name << ", cancellationToken);";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "WriteDoubleAsync(" << nullable_name << ", cancellationToken);";
+ break;
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase);
+ }
+ }
+ else if (type->is_enum())
+ {
+ out << "WriteI32Async((int)" << nullable_name << ", cancellationToken);";
+ }
+ out << endl;
+ }
+ else
+ {
+ printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", prefix.c_str(), tfield->get_name().c_str(), type_name(type).c_str());
+ }
+}
+
+void t_netstd_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix)
+{
+ (void)tstruct;
+ out << indent() << "await " << prefix << ".WriteAsync(oprot, cancellationToken);" << endl;
+}
+
+void t_netstd_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix)
+{
+ out << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_map())
+ {
+ out << indent() << "await oprot.WriteMapBeginAsync(new TMap(" << type_to_enum(static_cast<t_map*>(ttype)->get_key_type())
+ << ", " << type_to_enum(static_cast<t_map*>(ttype)->get_val_type()) << ", " << prefix
+ << ".Count), cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "await oprot.WriteSetBeginAsync(new TSet(" << type_to_enum(static_cast<t_set*>(ttype)->get_elem_type())
+ << ", " << prefix << ".Count), cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "await oprot.WriteListBeginAsync(new TList("
+ << type_to_enum(static_cast<t_list*>(ttype)->get_elem_type()) << ", " << prefix << ".Count), cancellationToken);"
+ << endl;
+ }
+
+ string iter = tmp("_iter");
+ if (ttype->is_map())
+ {
+ out << indent() << "foreach (" << type_name(static_cast<t_map*>(ttype)->get_key_type()) << " " << iter
+ << " in " << prefix << ".Keys)";
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "foreach (" << type_name(static_cast<t_set*>(ttype)->get_elem_type()) << " " << iter
+ << " in " << prefix << ")";
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "foreach (" << type_name(static_cast<t_list*>(ttype)->get_elem_type()) << " " << iter
+ << " in " << prefix << ")";
+ }
+
+ out << endl;
+ out << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_map())
+ {
+ generate_serialize_map_element(out, static_cast<t_map*>(ttype), iter, prefix);
+ }
+ else if (ttype->is_set())
+ {
+ generate_serialize_set_element(out, static_cast<t_set*>(ttype), iter);
+ }
+ else if (ttype->is_list())
+ {
+ generate_serialize_list_element(out, static_cast<t_list*>(ttype), iter);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ if (ttype->is_map())
+ {
+ out << indent() << "await oprot.WriteMapEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_set())
+ {
+ out << indent() << "await oprot.WriteSetEndAsync(cancellationToken);" << endl;
+ }
+ else if (ttype->is_list())
+ {
+ out << indent() << "await oprot.WriteListEndAsync(cancellationToken);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_netstd_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map)
+{
+ t_field kfield(tmap->get_key_type(), iter);
+ generate_serialize_field(out, &kfield, "");
+ t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+ generate_serialize_field(out, &vfield, "");
+}
+
+void t_netstd_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter)
+{
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+void t_netstd_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter)
+{
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+void t_netstd_generator::generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset)
+{
+ generate_netstd_property(out, tfield, isPublic, generateIsset, "_");
+}
+
+void t_netstd_generator::generate_netstd_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset, string fieldPrefix)
+{
+ if ((is_serialize_enabled() || is_wcf_enabled()) && isPublic)
+ {
+ out << indent() << "[DataMember(Order = 0)]" << endl;
+ }
+ bool is_required = field_is_required(tfield);
+ if (is_required)
+ {
+ out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type()) << " " << prop_name(tfield) << " { get; set; }" << endl;
+ }
+ else
+ {
+ out << indent() << (isPublic ? "public " : "private ") << type_name(tfield->get_type()) << " " << prop_name(tfield) << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ out << indent() << "get" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ bool use_nullable = false;
+
+ out << indent() << "return " << fieldPrefix + tfield->get_name() << ";" << endl;
+ indent_down();
+ out << indent() << "}" << endl
+ << indent() << "set" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (use_nullable)
+ {
+ if (generateIsset)
+ {
+ out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = value.HasValue;" << endl;
+ }
+ out << indent() << "if (value.HasValue) this." << fieldPrefix + tfield->get_name() << " = value.Value;" << endl;
+ }
+ else
+ {
+ if (generateIsset)
+ {
+ out << indent() << "__isset." << normalize_name(tfield->get_name()) << " = true;" << endl;
+ }
+ out << indent() << "this." << fieldPrefix + tfield->get_name() << " = value;" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ out << endl;
+}
+
+string t_netstd_generator::make_valid_csharp_identifier(string const& fromName)
+{
+ string str = fromName;
+ if (str.empty())
+ {
+ return str;
+ }
+
+ // tests rely on this
+ assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));
+
+ // if the first letter is a number, we add an additional underscore in front of it
+ char c = str.at(0);
+ if (('0' <= c) && (c <= '9'))
+ {
+ str = "_" + str;
+ }
+
+ // following chars: letter, number or underscore
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ c = str.at(i);
+ if (('A' > c || c > 'Z') && ('a' > c || c > 'z') && ('0' > c || c > '9') && '_' != c)
+ {
+ str.replace(i, 1, "_");
+ }
+ }
+
+ return str;
+}
+
+void t_netstd_generator::cleanup_member_name_mapping(void* scope)
+{
+ if (member_mapping_scopes.empty())
+ {
+ throw "internal error: cleanup_member_name_mapping() no scope active";
+ }
+
+ member_mapping_scope& active = member_mapping_scopes.back();
+ if (active.scope_member != scope)
+ {
+ throw "internal error: cleanup_member_name_mapping() called for wrong struct";
+ }
+
+ member_mapping_scopes.pop_back();
+}
+
+string t_netstd_generator::get_mapped_member_name(string name)
+{
+ if (!member_mapping_scopes.empty())
+ {
+ member_mapping_scope& active = member_mapping_scopes.back();
+ map<string, string>::iterator iter = active.mapping_table.find(name);
+ if (active.mapping_table.end() != iter)
+ {
+ return iter->second;
+ }
+ }
+
+ pverbose("no mapping for member %s\n", name.c_str());
+ return name;
+}
+
+void t_netstd_generator::prepare_member_name_mapping(t_struct* tstruct)
+{
+ prepare_member_name_mapping(tstruct, tstruct->get_members(), tstruct->get_name());
+}
+
+void t_netstd_generator::prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname)
+{
+ // begin new scope
+ member_mapping_scopes.emplace_back();
+ member_mapping_scope& active = member_mapping_scopes.back();
+ active.scope_member = scope;
+
+ // current C# generator policy:
+ // - prop names are always rendered with an Uppercase first letter
+ // - struct names are used as given
+ std::set<string> used_member_names;
+ vector<t_field*>::const_iterator iter;
+
+ // prevent name conflicts with struct (CS0542 error)
+ used_member_names.insert(structname);
+
+ // prevent name conflicts with known methods (THRIFT-2942)
+ used_member_names.insert("Read");
+ used_member_names.insert("Write");
+
+ for (iter = members.begin(); iter != members.end(); ++iter)
+ {
+ string oldname = (*iter)->get_name();
+ string newname = prop_name(*iter, true);
+ while (true)
+ {
+ // new name conflicts with another member
+ if (used_member_names.find(newname) != used_member_names.end())
+ {
+ pverbose("struct %s: member %s conflicts with another member\n", structname.c_str(), newname.c_str());
+ newname += '_';
+ continue;
+ }
+
+ // add always, this helps us to detect edge cases like
+ // different spellings ("foo" and "Foo") within the same struct
+ pverbose("struct %s: member mapping %s => %s\n", structname.c_str(), oldname.c_str(), newname.c_str());
+ active.mapping_table[oldname] = newname;
+ used_member_names.insert(newname);
+ break;
+ }
+ }
+}
+
+string t_netstd_generator::prop_name(t_field* tfield, bool suppress_mapping)
+{
+ string name(tfield->get_name());
+ if (suppress_mapping)
+ {
+ name[0] = toupper(name[0]);
+ }
+ else
+ {
+ name = get_mapped_member_name(name);
+ }
+ return name;
+}
+
+string t_netstd_generator::type_name(t_type* ttype)
+{
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+
+ if (ttype->is_base_type())
+ {
+ return base_type_name(static_cast<t_base_type*>(ttype));
+ }
+
+ if (ttype->is_map())
+ {
+ t_map* tmap = static_cast<t_map*>(ttype);
+ return "Dictionary<" + type_name(tmap->get_key_type()) + ", " + type_name(tmap->get_val_type()) + ">";
+ }
+
+ if (ttype->is_set())
+ {
+ t_set* tset = static_cast<t_set*>(ttype);
+ return "THashSet<" + type_name(tset->get_elem_type()) + ">";
+ }
+
+ if (ttype->is_list())
+ {
+ t_list* tlist = static_cast<t_list*>(ttype);
+ return "List<" + type_name(tlist->get_elem_type()) + ">";
+ }
+
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_)
+ {
+ string ns = program->get_namespace("netstd");
+ if (!ns.empty())
+ {
+ return ns + "." + normalize_name(ttype->get_name());
+ }
+ }
+
+ return normalize_name(ttype->get_name());
+}
+
+string t_netstd_generator::base_type_name(t_base_type* tbase)
+{
+ switch (tbase->get_base())
+ {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ {
+ if (tbase->is_binary())
+ {
+ return "byte[]";
+ }
+ return "string";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "sbyte";
+ case t_base_type::TYPE_I16:
+ return "short";
+ case t_base_type::TYPE_I32:
+ return "int";
+ case t_base_type::TYPE_I64:
+ return "long";
+ case t_base_type::TYPE_DOUBLE:
+ return "double";
+ default:
+ throw "compiler error: no C# name for base type " + t_base_type::t_base_name(tbase->get_base());
+ }
+}
+
+string t_netstd_generator::declare_field(t_field* tfield, bool init, string prefix)
+{
+ string result = type_name(tfield->get_type()) + " " + prefix + tfield->get_name();
+ if (init)
+ {
+ t_type* ttype = tfield->get_type();
+ while (ttype->is_typedef())
+ {
+ ttype = static_cast<t_typedef*>(ttype)->get_type();
+ }
+ if (ttype->is_base_type() && field_has_default(tfield))
+ {
+ std::ofstream dummy;
+ result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value());
+ }
+ else if (ttype->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(ttype)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ result += " = null";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = (double)0";
+ break;
+ }
+ }
+ else if (ttype->is_enum())
+ {
+ result += " = (" + type_name(ttype) + ")0";
+ }
+ else if (ttype->is_container())
+ {
+ result += " = new " + type_name(ttype) + "()";
+ }
+ else
+ {
+ result += " = new " + type_name(ttype) + "()";
+ }
+ }
+ return result + ";";
+}
+
+string t_netstd_generator::function_signature(t_function* tfunction, string prefix)
+{
+ t_type* ttype = tfunction->get_returntype();
+ return type_name(ttype) + " " + normalize_name(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + ")";
+}
+
+string t_netstd_generator::function_signature_async(t_function* tfunction, string prefix)
+{
+ t_type* ttype = tfunction->get_returntype();
+ string task = "Task";
+ if (!ttype->is_void())
+ {
+ task += "<" + type_name(ttype) + ">";
+ }
+
+ string result = task + " " + normalize_name(prefix + tfunction->get_name()) + "Async(";
+ string args = argument_list(tfunction->get_arglist());
+ result += args;
+ if (!args.empty())
+ {
+ result += ", ";
+ }
+ result += "CancellationToken cancellationToken = default(CancellationToken))";
+
+ return result;
+}
+
+string t_netstd_generator::argument_list(t_struct* tstruct)
+{
+ string result = "";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ result += ", ";
+ }
+ result += type_name((*f_iter)->get_type()) + " " + normalize_name((*f_iter)->get_name());
+ }
+ return result;
+}
+
+string t_netstd_generator::type_to_enum(t_type* type)
+{
+ while (type->is_typedef())
+ {
+ type = static_cast<t_typedef*>(type)->get_type();
+ }
+
+ if (type->is_base_type())
+ {
+ t_base_type::t_base tbase = static_cast<t_base_type*>(type)->get_base();
+ switch (tbase)
+ {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.String";
+ case t_base_type::TYPE_BOOL:
+ return "TType.Bool";
+ case t_base_type::TYPE_I8:
+ return "TType.Byte";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.Double";
+ }
+ }
+ else if (type->is_enum())
+ {
+ return "TType.I32";
+ }
+ else if (type->is_struct() || type->is_xception())
+ {
+ return "TType.Struct";
+ }
+ else if (type->is_map())
+ {
+ return "TType.Map";
+ }
+ else if (type->is_set())
+ {
+ return "TType.Set";
+ }
+ else if (type->is_list())
+ {
+ return "TType.List";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+void t_netstd_generator::generate_netstd_docstring_comment(ostream& out, string contents)
+{
+ docstring_comment(out, "/// <summary>" + endl, "/// ", contents, "/// </summary>" + endl);
+}
+
+void t_netstd_generator::generate_netstd_doc(ostream& out, t_field* field)
+{
+ if (field->get_type()->is_enum())
+ {
+ string combined_message = field->get_doc() + endl + "<seealso cref=\"" + get_enum_class_name(field->get_type()) + "\"/>";
+ generate_netstd_docstring_comment(out, combined_message);
+ }
+ else
+ {
+ generate_netstd_doc(out, static_cast<t_doc*>(field));
+ }
+}
+
+void t_netstd_generator::generate_netstd_doc(ostream& out, t_doc* tdoc)
+{
+ if (tdoc->has_doc())
+ {
+ generate_netstd_docstring_comment(out, tdoc->get_doc());
+ }
+}
+
+void t_netstd_generator::generate_netstd_doc(ostream& out, t_function* tfunction)
+{
+ if (tfunction->has_doc())
+ {
+ stringstream ps;
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter)
+ {
+ t_field* p = *p_iter;
+ ps << endl << "<param name=\"" << p->get_name() << "\">";
+ if (p->has_doc())
+ {
+ string str = p->get_doc();
+ str.erase(remove(str.begin(), str.end(), '\n'), str.end());
+ ps << str;
+ }
+ ps << "</param>";
+ }
+
+ docstring_comment(out,
+ "",
+ "/// ",
+ "<summary>" + endl + tfunction->get_doc() + "</summary>" + ps.str(),
+ "");
+ }
+}
+
+void t_netstd_generator::docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end)
+{
+ if (comment_start != "")
+ {
+ out << indent() << comment_start;
+ }
+
+ stringstream docs(contents, std::ios_base::in);
+
+ while (!(docs.eof() || docs.fail()))
+ {
+ char line[1024];
+ docs.getline(line, 1024);
+
+ // Just prnt a newline when the line & prefix are empty.
+ if (strlen(line) == 0 && line_prefix == "" && !docs.eof())
+ {
+ out << endl;
+ }
+ else if (strlen(line) > 0 || !docs.eof())
+ { // skip the empty last line
+ out << indent() << line_prefix << line << endl;
+ }
+ }
+ if (comment_end != "")
+ {
+ out << indent() << comment_end;
+ }
+}
+
+string t_netstd_generator::get_enum_class_name(t_type* type)
+{
+ string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL && program != program_)
+ {
+ package = program->get_namespace("netstd") + ".";
+ }
+ return package + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ netstd,
+ "C#",
+ " wcf: Adds bindings for WCF to generated classes.\n"
+ " serial: Add serialization support to generated classes.\n"
+ " union: Use new union typing, which includes a static read function for union types.\n"
+)
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.h
new file mode 100644
index 000000000..fd9e6c070
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_netstd_generator.h
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <cassert>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cctype>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+class t_netstd_generator : public t_oop_generator
+{
+
+ struct member_mapping_scope
+ {
+ public:
+ member_mapping_scope() : scope_member(0) { }
+ void* scope_member;
+ map<string, string> mapping_table;
+ };
+
+public:
+ t_netstd_generator(t_program* program, const map<string, string>& parsed_options, const string& option_string);
+
+ bool is_wcf_enabled() const;
+ bool is_nullable_enabled() const;
+ bool is_hashcode_enabled() const;
+ bool is_serialize_enabled() const;
+ bool is_union_enabled() const;
+ map<string, int> get_keywords_list() const;
+
+ // overrides
+ void init_generator();
+ void close_generator();
+ void generate_consts(vector<t_const*> consts);
+ void generate_consts(ostream& out, vector<t_const*> consts);
+ void generate_typedef(t_typedef* ttypedef);
+ void generate_enum(t_enum* tenum);
+ void generate_enum(ostream& out, t_enum* tenum);
+ void generate_struct(t_struct* tstruct);
+ void generate_xception(t_struct* txception);
+ void generate_service(t_service* tservice);
+
+ void generate_property(ostream& out, t_field* tfield, bool isPublic, bool generateIsset);
+ void generate_netstd_property(ostream& out, t_field* tfield, bool isPublic, bool includeIsset = true, string fieldPrefix = "");
+ bool print_const_value(ostream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval = false, bool needtype = false);
+ string render_const_value(ostream& out, string name, t_type* type, t_const_value* value);
+ void print_const_constructor(ostream& out, vector<t_const*> consts);
+ void print_const_def_value(ostream& out, string name, t_type* type, t_const_value* value);
+ void generate_netstd_struct(t_struct* tstruct, bool is_exception);
+ void generate_netstd_union(t_struct* tunion);
+ void generate_netstd_struct_definition(ostream& out, t_struct* tstruct, bool is_xception = false, bool in_class = false, bool is_result = false);
+ void generate_netstd_union_definition(ostream& out, t_struct* tunion);
+ void generate_netstd_union_class(ostream& out, t_struct* tunion, t_field* tfield);
+ void generate_netstd_wcffault(ostream& out, t_struct* tstruct);
+ void generate_netstd_struct_reader(ostream& out, t_struct* tstruct);
+ void generate_netstd_struct_result_writer(ostream& out, t_struct* tstruct);
+ void generate_netstd_struct_writer(ostream& out, t_struct* tstruct);
+ void generate_netstd_struct_tostring(ostream& out, t_struct* tstruct);
+ void generate_netstd_struct_equals(ostream& out, t_struct* tstruct);
+ void generate_netstd_struct_hashcode(ostream& out, t_struct* tstruct);
+ void generate_netstd_union_reader(ostream& out, t_struct* tunion);
+ void generate_function_helpers(ostream& out, t_function* tfunction);
+ void generate_service_interface(ostream& out, t_service* tservice);
+ void generate_service_helpers(ostream& out, t_service* tservice);
+ void generate_service_client(ostream& out, t_service* tservice);
+ void generate_service_server(ostream& out, t_service* tservice);
+ void generate_process_function_async(ostream& out, t_service* tservice, t_function* function);
+ void generate_deserialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_propertyless = false);
+ void generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix = "");
+ void generate_deserialize_container(ostream& out, t_type* ttype, string prefix = "");
+ void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix = "");
+ void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix = "");
+ void generate_deserialize_list_element(ostream& out, t_list* list, string prefix = "");
+ void generate_serialize_field(ostream& out, t_field* tfield, string prefix = "", bool is_propertyless = false);
+ void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix = "");
+ void generate_serialize_container(ostream& out, t_type* ttype, string prefix = "");
+ void generate_serialize_map_element(ostream& out, t_map* tmap, string iter, string map);
+ void generate_serialize_set_element(ostream& out, t_set* tmap, string iter);
+ void generate_serialize_list_element(ostream& out, t_list* tlist, string iter);
+ void generate_netstd_doc(ostream& out, t_field* field);
+ void generate_netstd_doc(ostream& out, t_doc* tdoc);
+ void generate_netstd_doc(ostream& out, t_function* tdoc);
+ void generate_netstd_docstring_comment(ostream& out, string contents);
+ void docstring_comment(ostream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end);
+ void start_netstd_namespace(ostream& out);
+ void end_netstd_namespace(ostream& out);
+
+ string netstd_type_usings() const;
+ string netstd_thrift_usings() const;
+
+ string type_name(t_type* ttype);
+ string base_type_name(t_base_type* tbase);
+ string declare_field(t_field* tfield, bool init = false, string prefix = "");
+ string function_signature_async(t_function* tfunction, string prefix = "");
+ string function_signature(t_function* tfunction, string prefix = "");
+ string argument_list(t_struct* tstruct);
+ string type_to_enum(t_type* ttype);
+ string prop_name(t_field* tfield, bool suppress_mapping = false);
+ string get_enum_class_name(t_type* type);
+
+private:
+ string namespace_name_;
+ string namespace_dir_;
+
+ bool nullable_;
+ bool union_;
+ bool hashcode_;
+ bool serialize_;
+ bool wcf_;
+
+ string wcf_namespace_;
+ map<string, int> netstd_keywords;
+ vector<member_mapping_scope> member_mapping_scopes;
+
+ void init_keywords();
+ string normalize_name(string name);
+ string make_valid_csharp_identifier(string const& fromName);
+ void prepare_member_name_mapping(t_struct* tstruct);
+ void prepare_member_name_mapping(void* scope, const vector<t_field*>& members, const string& structname);
+ void cleanup_member_name_mapping(void* scope);
+ string get_mapped_member_name(string oldname);
+};
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc
new file mode 100644
index 000000000..a3781e8ee
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc
@@ -0,0 +1,1762 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::ios;
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * OCaml code generator.
+ *
+ */
+class t_ocaml_generator : public t_oop_generator {
+public:
+ t_ocaml_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option ocaml:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-ocaml";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+ void generate_program() override;
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+ bool struct_member_persistent(t_field* tmember);
+ bool struct_member_omitable(t_field* tmember);
+ bool struct_member_default_cheaply_comparable(t_field* tmember);
+ std::string struct_member_copy_of(t_type* type, string what);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_ocaml_struct(t_struct* tstruct, bool is_exception);
+ void generate_ocaml_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false);
+ void generate_ocaml_struct_member(std::ostream& out, string tname, t_field* tmember);
+ void generate_ocaml_struct_sig(std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_ocaml_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_ocaml_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_ocaml_function_helpers(t_function* tfunction);
+ void generate_ocaml_method_copy(std::ostream& out, const vector<t_field*>& members);
+ void generate_ocaml_member_copy(std::ostream& out, t_field* member);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix);
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct);
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype);
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset);
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+ void generate_deserialize_type(std::ostream& out, t_type* type);
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string name = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string ocaml_autogen_comment();
+ std::string ocaml_imports();
+ std::string type_name(t_type* ttype);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string function_type(t_function* tfunc, bool method = false, bool options = false);
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string render_ocaml_type(t_type* type);
+
+private:
+ /**
+ * File streams
+ */
+
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_consts_;
+ ofstream_with_content_based_conditional_update f_service_;
+
+ ofstream_with_content_based_conditional_update f_types_i_;
+ ofstream_with_content_based_conditional_update f_service_i_;
+};
+
+/*
+ * This is necessary because we want typedefs to appear later,
+ * after all the types have been declared.
+ */
+void t_ocaml_generator::generate_program() {
+ // Initialize the generator
+ init_generator();
+
+ // Generate enums
+ vector<t_enum*> enums = program_->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ generate_enum(*en_iter);
+ }
+
+ // Generate structs
+ vector<t_struct*> structs = program_->get_structs();
+ vector<t_struct*>::iterator st_iter;
+ for (st_iter = structs.begin(); st_iter != structs.end(); ++st_iter) {
+ generate_struct(*st_iter);
+ }
+
+ // Generate xceptions
+ vector<t_struct*> xceptions = program_->get_xceptions();
+ vector<t_struct*>::iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ generate_xception(*x_iter);
+ }
+
+ // Generate typedefs
+ vector<t_typedef*> typedefs = program_->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ generate_typedef(*td_iter);
+ }
+
+ // Generate services
+ vector<t_service*> services = program_->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ service_name_ = get_service_name(*sv_iter);
+ generate_service(*sv_iter);
+ }
+
+ // Generate constants
+ vector<t_const*> consts = program_->get_consts();
+ generate_consts(consts);
+
+ // Close the generator
+ close_generator();
+}
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_ocaml_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ // Make output file
+ string f_types_name = get_out_dir() + program_name_ + "_types.ml";
+ f_types_.open(f_types_name.c_str());
+ string f_types_i_name = get_out_dir() + program_name_ + "_types.mli";
+ f_types_i_.open(f_types_i_name.c_str());
+
+ string f_consts_name = get_out_dir() + program_name_ + "_consts.ml";
+ f_consts_.open(f_consts_name.c_str());
+
+ // Print header
+ f_types_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl;
+ f_types_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl;
+ f_consts_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl << "open "
+ << capitalize(program_name_) << "_types" << endl;
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_ocaml_generator::ocaml_autogen_comment() {
+ return std::string("(*\n") + " Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "\n"
+ + " DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING\n" + "*)\n";
+}
+
+/**
+ * Prints standard thrift imports
+ */
+string t_ocaml_generator::ocaml_imports() {
+ return "open Thrift";
+}
+
+/**
+ * Closes the type files
+ */
+void t_ocaml_generator::close_generator() {
+ // Close types file
+ f_types_.close();
+}
+
+/**
+ * Generates a typedef. Ez.
+ *
+ * @param ttypedef The type definition
+ */
+void t_ocaml_generator::generate_typedef(t_typedef* ttypedef) {
+ f_types_ << indent() << "type " << decapitalize(ttypedef->get_symbolic()) << " = "
+ << render_ocaml_type(ttypedef->get_type()) << endl << endl;
+ f_types_i_ << indent() << "type " << decapitalize(ttypedef->get_symbolic()) << " = "
+ << render_ocaml_type(ttypedef->get_type()) << endl << endl;
+}
+
+/**
+ * Generates code for an enumerated type.
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_ocaml_generator::generate_enum(t_enum* tenum) {
+ indent(f_types_) << "module " << capitalize(tenum->get_name()) << " = " << endl << "struct"
+ << endl;
+ indent(f_types_i_) << "module " << capitalize(tenum->get_name()) << " : " << endl << "sig"
+ << endl;
+ indent_up();
+ indent(f_types_) << "type t = " << endl;
+ indent(f_types_i_) << "type t = " << endl;
+ indent_up();
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ string name = capitalize((*c_iter)->get_name());
+ indent(f_types_) << "| " << name << endl;
+ indent(f_types_i_) << "| " << name << endl;
+ }
+ indent_down();
+
+ indent(f_types_) << "let to_i = function" << endl;
+ indent(f_types_i_) << "val to_i : t -> Int32.t" << endl;
+ indent_up();
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ string name = capitalize((*c_iter)->get_name());
+ indent(f_types_) << "| " << name << " -> " << value << "l" << endl;
+ }
+ indent_down();
+
+ indent(f_types_) << "let of_i = function" << endl;
+ indent(f_types_i_) << "val of_i : Int32.t -> t" << endl;
+ indent_up();
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ string name = capitalize((*c_iter)->get_name());
+ indent(f_types_) << "| " << value << "l -> " << name << endl;
+ }
+ indent(f_types_) << "| _ -> raise Thrift_error" << endl;
+ indent_down();
+ indent_down();
+ indent(f_types_) << "end" << endl;
+ indent(f_types_i_) << "end" << endl;
+}
+
+/**
+ * Generate a constant value
+ */
+void t_ocaml_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = decapitalize(tconst->get_name());
+ t_const_value* value = tconst->get_value();
+
+ indent(f_consts_) << "let " << name << " = " << render_const_value(type, value) << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_ocaml_generator::render_const_value(t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream out;
+ // OCaml requires all floating point numbers contain a decimal point
+ out.setf(ios::showpoint);
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_I32:
+ out << value->get_integer() << "l";
+ break;
+ case t_base_type::TYPE_I64:
+ out << value->get_integer() << "L";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer() << ".0";
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ t_enum* tenum = (t_enum*)type;
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int val = (*c_iter)->get_value();
+ if (val == value->get_integer()) {
+ indent(out) << capitalize(tenum->get_name()) << "." << capitalize((*c_iter)->get_name());
+ break;
+ }
+ }
+ } else if (type->is_struct() || type->is_xception()) {
+ string cname = type_name(type);
+ string ct = tmp("_c");
+ out << endl;
+ indent_up();
+ indent(out) << "(let " << ct << " = new " << cname << " in" << endl;
+ indent_up();
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ string fname = v_iter->first->get_string();
+ out << indent();
+ out << ct << "#set_" << fname << " ";
+ out << render_const_value(field_type, v_iter->second);
+ out << ";" << endl;
+ }
+ indent(out) << ct << ")";
+ indent_down();
+ indent_down();
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ string hm = tmp("_hm");
+ out << endl;
+ indent_up();
+ indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl;
+ indent_up();
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string key = render_const_value(ktype, v_iter->first);
+ string val = render_const_value(vtype, v_iter->second);
+ indent(out) << "Hashtbl.add " << hm << " " << key << " " << val << ";" << endl;
+ }
+ indent(out) << hm << ")";
+ indent_down();
+ indent_down();
+ } else if (type->is_list()) {
+ t_type* etype;
+ etype = ((t_list*)type)->get_elem_type();
+ out << "[" << endl;
+ indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent();
+ out << render_const_value(etype, *v_iter);
+ out << ";" << endl;
+ }
+ indent_down();
+ indent(out) << "]";
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ string hm = tmp("_hm");
+ indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl;
+ indent_up();
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ string val = render_const_value(etype, *v_iter);
+ indent(out) << "Hashtbl.add " << hm << " " << val << " true;" << endl;
+ }
+ indent(out) << hm << ")" << endl;
+ indent_down();
+ out << endl;
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+ return out.str();
+}
+
+/**
+ * Generates a "struct"
+ */
+void t_ocaml_generator::generate_struct(t_struct* tstruct) {
+ generate_ocaml_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct, but also has an exception declaration.
+ *
+ * @param txception The struct definition
+ */
+void t_ocaml_generator::generate_xception(t_struct* txception) {
+ generate_ocaml_struct(txception, true);
+}
+
+/**
+ * Generates an OCaml struct
+ */
+void t_ocaml_generator::generate_ocaml_struct(t_struct* tstruct, bool is_exception) {
+ generate_ocaml_struct_definition(f_types_, tstruct, is_exception);
+ generate_ocaml_struct_sig(f_types_i_, tstruct, is_exception);
+}
+
+void t_ocaml_generator::generate_ocaml_method_copy(ostream& out, const vector<t_field*>& members) {
+ vector<t_field*>::const_iterator m_iter;
+
+ /* Create a copy of the current object */
+ indent(out) << "method copy =" << endl;
+ indent_up();
+ indent_up();
+ indent(out) << "let _new = Oo.copy self in" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+ generate_ocaml_member_copy(out, *m_iter);
+
+ indent_down();
+ indent(out) << "_new" << endl;
+ indent_down();
+}
+
+string t_ocaml_generator::struct_member_copy_of(t_type* type, string what) {
+ if (type->is_struct() || type->is_xception()) {
+ return what + string("#copy");
+ }
+ if (type->is_map()) {
+ string copy_of_k = struct_member_copy_of(((t_map*)type)->get_key_type(), "k");
+ string copy_of_v = struct_member_copy_of(((t_map*)type)->get_val_type(), "v");
+
+ if (copy_of_k == "k" && copy_of_v == "v") {
+ return string("(Hashtbl.copy ") + what + string(")");
+ } else {
+ return string(
+ "((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v "
+ "-> Hashtbl.add nh ") + copy_of_k + string(" ") + copy_of_v + string(") oh; nh) ")
+ + what + ")";
+ }
+ }
+ if (type->is_set()) {
+ string copy_of = struct_member_copy_of(((t_set*)type)->get_elem_type(), "k");
+
+ if (copy_of == "k") {
+ return string("(Hashtbl.copy ") + what + string(")");
+ } else {
+ return string(
+ "((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v "
+ "-> Hashtbl.add nh ") + copy_of + string(" true") + string(") oh; nh) ") + what
+ + ")";
+ }
+ }
+ if (type->is_list()) {
+ string copy_of = struct_member_copy_of(((t_list*)type)->get_elem_type(), "x");
+ if (copy_of != "x") {
+ return string("(List.map (fun x -> ") + copy_of + string(") ") + what + string(")");
+ } else {
+ return what;
+ }
+ }
+ return what;
+}
+
+void t_ocaml_generator::generate_ocaml_member_copy(ostream& out, t_field* tmember) {
+ string mname = decapitalize(tmember->get_name());
+ t_type* type = get_true_type(tmember->get_type());
+
+ string grab_field = string("self#grab_") + mname;
+ string copy_of = struct_member_copy_of(type, grab_field);
+ if (copy_of != grab_field) {
+ indent(out);
+ if (!struct_member_persistent(tmember)) {
+ out << "if _" << mname << " <> None then" << endl;
+ indent(out) << " ";
+ }
+ out << "_new#set_" << mname << " " << copy_of << ";" << endl;
+ }
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_ocaml_generator::generate_ocaml_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ string tname = type_name(tstruct);
+ indent(out) << "class " << tname << " =" << endl;
+ indent(out) << "object (self)" << endl;
+
+ indent_up();
+
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_ocaml_struct_member(out, tname, (*m_iter));
+ out << endl;
+ }
+ }
+ generate_ocaml_method_copy(out, members);
+ generate_ocaml_struct_writer(out, tstruct);
+ indent_down();
+ indent(out) << "end" << endl;
+
+ if (is_exception) {
+ indent(out) << "exception " << capitalize(tname) << " of " << tname << endl;
+ }
+
+ generate_ocaml_struct_reader(out, tstruct);
+}
+
+/**
+ * Generates a structure member for a thrift data type.
+ *
+ * @param tname Name of the parent structure for the member
+ * @param tmember Member definition
+ */
+void t_ocaml_generator::generate_ocaml_struct_member(ostream& out,
+ string tname,
+ t_field* tmember) {
+ string x = tmp("_x");
+ string mname = decapitalize(tmember->get_name());
+
+ indent(out) << "val mutable _" << mname << " : " << render_ocaml_type(tmember->get_type());
+ t_const_value* val = tmember->get_value();
+ if (val) {
+ if (struct_member_persistent(tmember))
+ out << " = " << render_const_value(tmember->get_type(), tmember->get_value()) << endl;
+ else
+ out << " option = Some " << render_const_value(tmember->get_type(), tmember->get_value())
+ << endl;
+ } else {
+ // assert(!struct_member_persistent(tmember))
+ out << " option = None" << endl;
+ }
+
+ if (struct_member_persistent(tmember)) {
+ indent(out) << "method get_" << mname << " = Some _" << mname << endl;
+ indent(out) << "method grab_" << mname << " = _" << mname << endl;
+ indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- " << x << endl;
+ } else {
+ indent(out) << "method get_" << mname << " = _" << mname << endl;
+ indent(out) << "method grab_" << mname << " = match _" << mname
+ << " with None->raise (Field_empty \"" << tname << "." << mname << "\") | Some "
+ << x << " -> " << x << endl;
+ indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- Some " << x
+ << endl;
+ indent(out) << "method unset_" << mname << " = _" << mname << " <- None" << endl;
+ }
+
+ indent(out) << "method reset_" << mname << " = _" << mname << " <- ";
+ if (val) {
+ if (struct_member_persistent(tmember))
+ out << render_const_value(tmember->get_type(), tmember->get_value()) << endl;
+ else
+ out << "Some " << render_const_value(tmember->get_type(), tmember->get_value()) << endl;
+ } else {
+ out << "None" << endl;
+ }
+}
+
+/**
+ * Check whether a member of the structure can not have undefined value
+ *
+ * @param tmember Member definition
+ */
+bool t_ocaml_generator::struct_member_persistent(t_field* tmember) {
+ t_const_value* val = tmember->get_value();
+ return (val ? true : false);
+}
+
+/**
+ * Check whether a member of the structure can be skipped during encoding
+ *
+ * @param tmember Member definition
+ */
+bool t_ocaml_generator::struct_member_omitable(t_field* tmember) {
+ return (tmember->get_req() != t_field::T_REQUIRED);
+}
+
+/**
+ * Figure out whether a member of the structure has
+ * a cheaply comparable default value.
+ *
+ * @param tmember Member definition
+ */
+bool t_ocaml_generator::struct_member_default_cheaply_comparable(t_field* tmember) {
+ t_type* type = get_true_type(tmember->get_type());
+ t_const_value* val = tmember->get_value();
+ if (!val) {
+ return false;
+ } else if (type->is_base_type()) {
+ // Base types are generally cheaply compared for structural equivalence.
+ switch (((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_DOUBLE:
+ if (val->get_double() == 0.0)
+ return true;
+ else
+ return false;
+ default:
+ return true;
+ }
+ } else if (type->is_list()) {
+ // Empty lists are cheaply compared for structural equivalence.
+ // Is empty list?
+ if (val->get_list().size() == 0)
+ return true;
+ else
+ return false;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_ocaml_generator::generate_ocaml_struct_sig(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ string tname = type_name(tstruct);
+ indent(out) << "class " << tname << " :" << endl;
+ indent(out) << "object ('a)" << endl;
+
+ indent_up();
+
+ string x = tmp("_x");
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string mname = decapitalize((*m_iter)->get_name());
+ string type = render_ocaml_type((*m_iter)->get_type());
+ indent(out) << "method get_" << mname << " : " << type << " option" << endl;
+ indent(out) << "method grab_" << mname << " : " << type << endl;
+ indent(out) << "method set_" << mname << " : " << type << " -> unit" << endl;
+ if (!struct_member_persistent(*m_iter))
+ indent(out) << "method unset_" << mname << " : unit" << endl;
+ indent(out) << "method reset_" << mname << " : unit" << endl;
+ }
+ }
+ indent(out) << "method copy : 'a" << endl;
+ indent(out) << "method write : Protocol.t -> unit" << endl;
+ indent_down();
+ indent(out) << "end" << endl;
+
+ if (is_exception) {
+ indent(out) << "exception " << capitalize(tname) << " of " << tname << endl;
+ }
+
+ indent(out) << "val read_" << tname << " : Protocol.t -> " << tname << endl;
+}
+
+/**
+ * Generates the read method for a struct
+ */
+void t_ocaml_generator::generate_ocaml_struct_reader(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ string sname = type_name(tstruct);
+ string str = tmp("_str");
+ string t = tmp("_t");
+ string id = tmp("_id");
+ indent(out) << "let rec read_" << sname << " (iprot : Protocol.t) =" << endl;
+ indent_up();
+ indent(out) << "let " << str << " = new " << sname << " in" << endl;
+ indent_up();
+ indent(out) << "ignore(iprot#readStructBegin);" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "(try while true do" << endl;
+ indent_up();
+ indent_up();
+
+ // Read beginning field marker
+ indent(out) << "let (_," << t << "," << id << ") = iprot#readFieldBegin in" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if " << t << " = Protocol.T_STOP then" << endl;
+ indent_up();
+ indent(out) << "raise Break" << endl;
+ indent_down();
+ indent(out) << "else ();" << endl;
+
+ indent(out) << "(match " << id << " with " << endl;
+ indent_up();
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "| " << (*f_iter)->get_key() << " -> (";
+ out << "if " << t << " = " << type_to_enum((*f_iter)->get_type()) << " then" << endl;
+ indent_up();
+ indent_up();
+ generate_deserialize_field(out, *f_iter, str);
+ indent_down();
+ out << indent() << "else" << endl << indent() << " iprot#skip " << t << ")" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ out << indent() << "| _ -> "
+ << "iprot#skip " << t << ");" << endl;
+ indent_down();
+ // Read field end marker
+ indent(out) << "iprot#readFieldEnd;" << endl;
+ indent_down();
+ indent(out) << "done; ()" << endl;
+ indent_down();
+ indent(out) << "with Break -> ());" << endl;
+
+ indent(out) << "iprot#readStructEnd;" << endl;
+
+ indent(out) << str << endl << endl;
+ indent_down();
+ indent_down();
+}
+
+void t_ocaml_generator::generate_ocaml_struct_writer(ostream& out, t_struct* tstruct) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+ string str = tmp("_str");
+ string f = tmp("_f");
+
+ indent(out) << "method write (oprot : Protocol.t) =" << endl;
+ indent_up();
+ indent(out) << "oprot#writeStructBegin \"" << name << "\";" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* tmember = (*f_iter);
+ string mname = "_" + decapitalize(tmember->get_name());
+ string _v;
+
+ if (struct_member_persistent(tmember)) {
+
+ if (struct_member_omitable(tmember) && struct_member_default_cheaply_comparable(tmember)) {
+ _v = "_v";
+ // Avoid redundant encoding of members having default values.
+ indent(out) << "(match " << mname << " with "
+ << render_const_value(tmember->get_type(), tmember->get_value()) << " -> () | "
+ << _v << " -> " << endl;
+ } else {
+ _v = mname;
+ indent(out) << "(" << endl;
+ }
+
+ } else {
+
+ indent(out) << "(match " << mname << " with ";
+
+ if (struct_member_omitable(tmember)) {
+ out << "None -> ()";
+
+ if (struct_member_default_cheaply_comparable(tmember)) {
+ // Avoid redundant encoding of members having default values.
+ out << " | Some " << render_const_value(tmember->get_type(), tmember->get_value())
+ << " -> ()";
+ }
+ out << " | Some _v -> " << endl;
+ } else {
+ out << endl;
+ indent(out) << "| None -> raise (Field_empty \"" << type_name(tstruct) << "." << mname
+ << "\")" << endl;
+ indent(out) << "| Some _v -> " << endl;
+ }
+
+ _v = "_v";
+ }
+ indent_up();
+ // Write field header
+ indent(out) << "oprot#writeFieldBegin(\"" << tmember->get_name() << "\","
+ << type_to_enum(tmember->get_type()) << "," << tmember->get_key() << ");" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, tmember, _v);
+
+ // Write field closer
+ indent(out) << "oprot#writeFieldEnd" << endl;
+
+ indent_down();
+ indent(out) << ");" << endl;
+ }
+
+ // Write the struct map
+ out << indent() << "oprot#writeFieldStop;" << endl << indent() << "oprot#writeStructEnd" << endl;
+
+ indent_down();
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_ocaml_generator::generate_service(t_service* tservice) {
+ string f_service_name = get_out_dir() + capitalize(service_name_) + ".ml";
+ f_service_.open(f_service_name.c_str());
+ string f_service_i_name = get_out_dir() + capitalize(service_name_) + ".mli";
+ f_service_i_.open(f_service_i_name.c_str());
+
+ f_service_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl;
+ f_service_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl;
+
+ /* if (tservice->get_extends() != NULL) {
+ f_service_ <<
+ "open " << capitalize(tservice->get_extends()->get_name()) << endl;
+ f_service_i_ <<
+ "open " << capitalize(tservice->get_extends()->get_name()) << endl;
+ }
+ */
+ f_service_ << "open " << capitalize(program_name_) << "_types" << endl << endl;
+
+ f_service_i_ << "open " << capitalize(program_name_) << "_types" << endl << endl;
+
+ // Generate the three main parts of the service
+ generate_service_helpers(tservice);
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+
+ // Close service file
+ f_service_.close();
+ f_service_i_.close();
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_ocaml_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ indent(f_service_) << "(* HELPER FUNCTIONS AND STRUCTURES *)" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_ocaml_struct_definition(f_service_, ts, false);
+ generate_ocaml_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_ocaml_generator::generate_ocaml_function_helpers(t_function* tfunction) {
+ t_struct result(program_, decapitalize(tfunction->get_name()) + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+ generate_ocaml_struct_definition(f_service_, &result, false);
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_ocaml_generator::generate_service_interface(t_service* tservice) {
+ f_service_ << indent() << "class virtual iface =" << endl << "object (self)" << endl;
+ f_service_i_ << indent() << "class virtual iface :" << endl << "object" << endl;
+
+ indent_up();
+
+ if (tservice->get_extends() != NULL) {
+ string extends = type_name(tservice->get_extends());
+ indent(f_service_) << "inherit " << extends << ".iface" << endl;
+ indent(f_service_i_) << "inherit " << extends << ".iface" << endl;
+ }
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string ft = function_type(*f_iter, true, true);
+ f_service_ << indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : "
+ << ft << endl;
+ f_service_i_ << indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : "
+ << ft << endl;
+ }
+ indent_down();
+ indent(f_service_) << "end" << endl << endl;
+ indent(f_service_i_) << "end" << endl << endl;
+}
+
+/**
+ * Generates a service client definition. Note that in OCaml, the client doesn't implement iface.
+ *This is because
+ * The client does not (and should not have to) deal with arguments being None.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_ocaml_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ indent(f_service_) << "class client (iprot : Protocol.t) (oprot : Protocol.t) =" << endl
+ << "object (self)" << endl;
+ indent(f_service_i_) << "class client : Protocol.t -> Protocol.t -> " << endl << "object" << endl;
+ indent_up();
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ indent(f_service_) << "inherit " << extends << ".client iprot oprot as super" << endl;
+ indent(f_service_i_) << "inherit " << extends << ".client" << endl;
+ }
+ indent(f_service_) << "val mutable seqid = 0" << endl;
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ indent(f_service_) << "method " << function_signature(*f_iter) << " = " << endl;
+ indent(f_service_i_) << "method " << decapitalize((*f_iter)->get_name()) << " : "
+ << function_type(*f_iter, true, false) << endl;
+ indent_up();
+ indent(f_service_) << "self#send_" << funname;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << " " << decapitalize((*fld_iter)->get_name());
+ }
+ f_service_ << ";" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ f_service_ << "self#recv_" << funname << endl;
+ }
+ indent_down();
+
+ indent(f_service_) << "method private send_" << function_signature(*f_iter) << " = " << endl;
+ indent_up();
+
+ std::string argsname = decapitalize((*f_iter)->get_name() + "_args");
+
+ // Serialize the request header
+ f_service_ << indent() << "oprot#writeMessageBegin (\"" << (*f_iter)->get_name() << "\", "
+ << ((*f_iter)->is_oneway() ? "Protocol.ONEWAY" : "Protocol.CALL") << ", seqid);"
+ << endl;
+
+ f_service_ << indent() << "let args = new " << argsname << " in" << endl;
+ indent_up();
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args#set_" << (*fld_iter)->get_name() << " "
+ << (*fld_iter)->get_name() << ";" << endl;
+ }
+
+ // Write to the stream
+ f_service_ << indent() << "args#write oprot;" << endl << indent() << "oprot#writeMessageEnd;"
+ << endl << indent() << "oprot#getTransport#flush" << endl;
+
+ indent_down();
+ indent_down();
+
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultname = decapitalize((*f_iter)->get_name() + "_result");
+ t_struct noargs(program_);
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ // Open function
+ f_service_ << indent() << "method private " << function_signature(&recv_function) << " ="
+ << endl;
+ indent_up();
+
+ // TODO(mcslee): Validate message reply here, seq ids etc.
+
+ f_service_ << indent() << "let (fname, mtype, rseqid) = iprot#readMessageBegin in" << endl;
+ indent_up();
+ f_service_ << indent() << "(if mtype = Protocol.EXCEPTION then" << endl << indent()
+ << " let x = Application_Exn.read iprot in" << endl;
+ indent_up();
+ f_service_ << indent() << " (iprot#readMessageEnd;" << indent()
+ << " raise (Application_Exn.E x))" << endl;
+ indent_down();
+ f_service_ << indent() << "else ());" << endl;
+ string res = "_";
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+
+ if (!(*f_iter)->get_returntype()->is_void() || xceptions.size() > 0) {
+ res = "result";
+ }
+ f_service_ << indent() << "let " << res << " = read_" << resultname << " iprot in" << endl;
+ indent_up();
+ f_service_ << indent() << "iprot#readMessageEnd;" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "match result#get_success with Some v -> v | None -> (" << endl;
+ indent_up();
+ }
+
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "(match result#get_" << (*x_iter)->get_name()
+ << " with None -> () | Some _v ->" << endl;
+ indent(f_service_) << " raise (" << capitalize(type_name((*x_iter)->get_type()))
+ << " _v));" << endl;
+ }
+
+ // Careful, only return _result if not a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "()" << endl;
+ } else {
+ f_service_
+ << indent()
+ << "raise (Application_Exn.E (Application_Exn.create Application_Exn.MISSING_RESULT \""
+ << (*f_iter)->get_name() << " failed: unknown result\")))" << endl;
+ indent_down();
+ }
+
+ // Close function
+ indent_down();
+ indent_down();
+ indent_down();
+ }
+ }
+
+ indent_down();
+ indent(f_service_) << "end" << endl << endl;
+ indent(f_service_i_) << "end" << endl << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_ocaml_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ // Generate the header portion
+ indent(f_service_) << "class processor (handler : iface) =" << endl << indent() << "object (self)"
+ << endl;
+ indent(f_service_i_) << "class processor : iface ->" << endl << indent() << "object" << endl;
+ indent_up();
+
+ f_service_ << indent() << "inherit Processor.t" << endl << endl;
+ f_service_i_ << indent() << "inherit Processor.t" << endl << endl;
+ string extends = "";
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ indent(f_service_) << "inherit " + extends + ".processor (handler :> " + extends + ".iface)"
+ << endl;
+ indent(f_service_i_) << "inherit " + extends + ".processor" << endl;
+ }
+
+ if (extends.empty()) {
+ indent(f_service_) << "val processMap = Hashtbl.create " << functions.size() << endl;
+ }
+ indent(f_service_i_)
+ << "val processMap : (string, int * Protocol.t * Protocol.t -> unit) Hashtbl.t" << endl;
+
+ // Generate the server implementation
+ indent(f_service_) << "method process iprot oprot =" << endl;
+ indent(f_service_i_) << "method process : Protocol.t -> Protocol.t -> bool" << endl;
+ indent_up();
+
+ f_service_ << indent() << "let (name, typ, seqid) = iprot#readMessageBegin in" << endl;
+ indent_up();
+ // TODO(mcslee): validate message
+
+ // HOT: dictionary function lookup
+ f_service_ << indent() << "if Hashtbl.mem processMap name then" << endl << indent()
+ << " (Hashtbl.find processMap name) (seqid, iprot, oprot)" << endl << indent()
+ << "else (" << endl << indent() << " iprot#skip(Protocol.T_STRUCT);" << endl
+ << indent() << " iprot#readMessageEnd;" << endl << indent()
+ << " let x = Application_Exn.create Application_Exn.UNKNOWN_METHOD (\"Unknown "
+ "function \"^name) in" << endl << indent()
+ << " oprot#writeMessageBegin(name, Protocol.EXCEPTION, seqid);" << endl << indent()
+ << " x#write oprot;" << endl << indent() << " oprot#writeMessageEnd;" << endl
+ << indent() << " oprot#getTransport#flush" << endl << indent() << ");" << endl;
+
+ // Read end of args field, the T_STOP, and the struct close
+ f_service_ << indent() << "true" << endl;
+ indent_down();
+ indent_down();
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent(f_service_) << "initializer" << endl;
+ indent_up();
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "Hashtbl.add processMap \"" << (*f_iter)->get_name()
+ << "\" self#process_" << (*f_iter)->get_name() << ";" << endl;
+ }
+ indent_down();
+
+ indent_down();
+ indent(f_service_) << "end" << endl << endl;
+ indent(f_service_i_) << "end" << endl << endl;
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_ocaml_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open function
+ indent(f_service_) << "method private process_" << tfunction->get_name()
+ << " (seqid, iprot, oprot) =" << endl;
+ indent_up();
+
+ string argsname = decapitalize(tfunction->get_name()) + "_args";
+ string resultname = decapitalize(tfunction->get_name()) + "_result";
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ string args = "args";
+ if (fields.size() == 0) {
+ args = "_";
+ }
+
+ f_service_ << indent() << "let " << args << " = read_" << argsname << " iprot in" << endl;
+ indent_up();
+ f_service_ << indent() << "iprot#readMessageEnd;" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "let result = new " << resultname << " in" << endl;
+ indent_up();
+ }
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "(try" << endl;
+ indent_up();
+ }
+
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result#set_success ";
+ }
+ f_service_ << "(handler#" << tfunction->get_name();
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ f_service_ << " args#get_" << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ if (xceptions.size() > 0) {
+ indent_down();
+ indent(f_service_) << "with" << endl;
+ indent_up();
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "| " << capitalize(type_name((*x_iter)->get_type())) << " "
+ << (*x_iter)->get_name() << " -> " << endl;
+ indent_up();
+ indent_up();
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "result#set_" << (*x_iter)->get_name() << " "
+ << (*x_iter)->get_name() << endl;
+ } else {
+ indent(f_service_) << "()";
+ }
+ indent_down();
+ indent_down();
+ }
+ indent_down();
+ f_service_ << indent() << ");" << endl;
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "()" << endl;
+ indent_down();
+ indent_down();
+ return;
+ }
+
+ f_service_ << indent() << "oprot#writeMessageBegin (\"" << tfunction->get_name()
+ << "\", Protocol.REPLY, seqid);" << endl << indent() << "result#write oprot;" << endl
+ << indent() << "oprot#writeMessageEnd;" << endl << indent()
+ << "oprot#getTransport#flush" << endl;
+
+ // Close function
+ indent_down();
+ indent_down();
+ indent_down();
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_ocaml_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = tfield->get_type();
+
+ string name = decapitalize(tfield->get_name());
+ indent(out) << prefix << "#set_" << name << " ";
+ generate_deserialize_type(out, type);
+ out << endl;
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_ocaml_generator::generate_deserialize_type(ostream& out, t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE";
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type);
+ } else if (type->is_base_type()) {
+ out << "iprot#";
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct";
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "readString";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble";
+ break;
+ default:
+ throw "compiler error: no ocaml name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ string ename = capitalize(type->get_name());
+ out << "(" << ename << ".of_i iprot#readI32)";
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE TYPE '%s'\n", type->get_name().c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, calling read()
+ */
+void t_ocaml_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct) {
+ string prefix = "";
+ t_program* program = tstruct->get_program();
+ if (program != NULL && program != program_) {
+ prefix = capitalize(program->get_name()) + "_types.";
+ }
+ string name = decapitalize(tstruct->get_name());
+ out << "(" << prefix << "read_" << name << " iprot)";
+}
+
+/**
+ * Serialize a container by writing out the header followed by
+ * data and then a footer.
+ */
+void t_ocaml_generator::generate_deserialize_container(ostream& out, t_type* ttype) {
+ string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+ string con = tmp("_con");
+
+ t_field fsize(g_type_i32, size);
+ t_field fktype(g_type_i8, ktype);
+ t_field fvtype(g_type_i8, vtype);
+ t_field fetype(g_type_i8, etype);
+
+ out << endl;
+ indent_up();
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ indent(out) << "(let (" << ktype << "," << vtype << "," << size << ") = iprot#readMapBegin in"
+ << endl;
+ indent(out) << "let " << con << " = Hashtbl.create " << size << " in" << endl;
+ indent_up();
+ indent(out) << "for i = 1 to " << size << " do" << endl;
+ indent_up();
+ indent(out) << "let _k = ";
+ generate_deserialize_type(out, ((t_map*)ttype)->get_key_type());
+ out << " in" << endl;
+ indent(out) << "let _v = ";
+ generate_deserialize_type(out, ((t_map*)ttype)->get_val_type());
+ out << " in" << endl;
+ indent_up();
+ indent(out) << "Hashtbl.add " << con << " _k _v" << endl;
+ indent_down();
+ indent_down();
+ indent(out) << "done; iprot#readMapEnd; " << con << ")";
+ indent_down();
+ } else if (ttype->is_set()) {
+ indent(out) << "(let (" << etype << "," << size << ") = iprot#readSetBegin in" << endl;
+ indent(out) << "let " << con << " = Hashtbl.create " << size << " in" << endl;
+ indent_up();
+ indent(out) << "for i = 1 to " << size << " do" << endl;
+ indent_up();
+ indent(out) << "Hashtbl.add " << con << " ";
+ generate_deserialize_type(out, ((t_set*)ttype)->get_elem_type());
+ out << " true" << endl;
+ indent_down();
+ indent(out) << "done; iprot#readSetEnd; " << con << ")";
+ indent_down();
+ } else if (ttype->is_list()) {
+ indent(out) << "(let (" << etype << "," << size << ") = iprot#readListBegin in" << endl;
+ indent_up();
+ indent(out) << "let " << con << " = (Array.to_list (Array.init " << size << " (fun _ -> ";
+ generate_deserialize_type(out, ((t_list*)ttype)->get_elem_type());
+ out << "))) in" << endl;
+ indent_up();
+ indent(out) << "iprot#readListEnd; " << con << ")";
+ indent_down();
+ indent_down();
+ }
+ indent_down();
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_ocaml_generator::generate_serialize_field(ostream& out, t_field* tfield, string name) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + tfield->get_name();
+ }
+
+ if (name.length() == 0) {
+ name = decapitalize(tfield->get_name());
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ indent(out) << "oprot#";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "writeString(" << name << ")";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ")";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ")";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ")";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ")";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ")";
+ break;
+ default:
+ throw "compiler error: no ocaml name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ string ename = capitalize(type->get_name());
+ out << "writeI32(" << ename << ".to_i " << name << ")";
+ }
+
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+ out << ";" << endl;
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_ocaml_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << prefix << "#write(oprot)";
+}
+
+void t_ocaml_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ if (ttype->is_map()) {
+ indent(out) << "oprot#writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ",";
+ out << type_to_enum(((t_map*)ttype)->get_val_type()) << ",";
+ out << "Hashtbl.length " << prefix << ");" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot#writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ",";
+ out << "Hashtbl.length " << prefix << ");" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot#writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
+ << ",";
+ out << "List.length " << prefix << ");" << endl;
+ }
+
+ if (ttype->is_map()) {
+ string kiter = tmp("_kiter");
+ string viter = tmp("_viter");
+ indent(out) << "Hashtbl.iter (fun " << kiter << " -> fun " << viter << " -> " << endl;
+ indent_up();
+ generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+ indent_down();
+ indent(out) << ") " << prefix << ";" << endl;
+ } else if (ttype->is_set()) {
+ string iter = tmp("_iter");
+ indent(out) << "Hashtbl.iter (fun " << iter << " -> fun _ -> ";
+ indent_up();
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ indent_down();
+ indent(out) << ") " << prefix << ";" << endl;
+ } else if (ttype->is_list()) {
+ string iter = tmp("_iter");
+ indent(out) << "List.iter (fun " << iter << " -> ";
+ indent_up();
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ indent_down();
+ indent(out) << ") " << prefix << ";" << endl;
+ }
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot#writeMapEnd";
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot#writeSetEnd";
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot#writeListEnd";
+ }
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_ocaml_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), kiter);
+ generate_serialize_field(out, &kfield);
+
+ t_field vfield(tmap->get_val_type(), viter);
+ generate_serialize_field(out, &vfield);
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_ocaml_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield);
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_ocaml_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield);
+}
+
+/**
+ * Renders a function signature of the form 'name args'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_ocaml_generator::function_signature(t_function* tfunction, string prefix) {
+ return prefix + decapitalize(tfunction->get_name()) + " "
+ + argument_list(tfunction->get_arglist());
+}
+
+string t_ocaml_generator::function_type(t_function* tfunc, bool method, bool options) {
+ string result = "";
+
+ const vector<t_field*>& fields = tfunc->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result += render_ocaml_type((*f_iter)->get_type());
+ if (options)
+ result += " option";
+ result += " -> ";
+ }
+ if (fields.empty() && !method) {
+ result += "unit -> ";
+ }
+ result += render_ocaml_type(tfunc->get_returntype());
+ return result;
+}
+
+/**
+ * Renders a field list
+ */
+string t_ocaml_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += " ";
+ }
+ result += (*f_iter)->get_name();
+ }
+ return result;
+}
+
+string t_ocaml_generator::type_name(t_type* ttype) {
+ string prefix = "";
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_) {
+ if (!ttype->is_service()) {
+ prefix = capitalize(program->get_name()) + "_types.";
+ }
+ }
+
+ string name = ttype->get_name();
+ if (ttype->is_service()) {
+ name = capitalize(name);
+ } else {
+ name = decapitalize(name);
+ }
+ return prefix + name;
+}
+
+/**
+ * Converts the parse type to a Protocol.t_type enum
+ */
+string t_ocaml_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "Protocol.T_VOID";
+ case t_base_type::TYPE_STRING:
+ return "Protocol.T_STRING";
+ case t_base_type::TYPE_BOOL:
+ return "Protocol.T_BOOL";
+ case t_base_type::TYPE_I8:
+ return "Protocol.T_BYTE";
+ case t_base_type::TYPE_I16:
+ return "Protocol.T_I16";
+ case t_base_type::TYPE_I32:
+ return "Protocol.T_I32";
+ case t_base_type::TYPE_I64:
+ return "Protocol.T_I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "Protocol.T_DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "Protocol.T_I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "Protocol.T_STRUCT";
+ } else if (type->is_map()) {
+ return "Protocol.T_MAP";
+ } else if (type->is_set()) {
+ return "Protocol.T_SET";
+ } else if (type->is_list()) {
+ return "Protocol.T_LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Converts the parse type to an ocaml type
+ */
+string t_ocaml_generator::render_ocaml_type(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "unit";
+ case t_base_type::TYPE_STRING:
+ return "string";
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "int";
+ case t_base_type::TYPE_I16:
+ return "int";
+ case t_base_type::TYPE_I32:
+ return "Int32.t";
+ case t_base_type::TYPE_I64:
+ return "Int64.t";
+ case t_base_type::TYPE_DOUBLE:
+ return "float";
+ }
+ } else if (type->is_enum()) {
+ return capitalize(((t_enum*)type)->get_name()) + ".t";
+ } else if (type->is_struct() || type->is_xception()) {
+ return type_name((t_struct*)type);
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ return "(" + render_ocaml_type(ktype) + "," + render_ocaml_type(vtype) + ") Hashtbl.t";
+ } else if (type->is_set()) {
+ t_type* etype = ((t_set*)type)->get_elem_type();
+ return "(" + render_ocaml_type(etype) + ",bool) Hashtbl.t";
+ } else if (type->is_list()) {
+ t_type* etype = ((t_list*)type)->get_elem_type();
+ return render_ocaml_type(etype) + " list";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(ocaml, "OCaml", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h
new file mode 100644
index 000000000..f8da54792
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_OOP_GENERATOR_H
+#define T_OOP_GENERATOR_H
+
+#include <string>
+#include <iostream>
+
+#include "thrift/common.h"
+#include "thrift/generate/t_generator.h"
+
+#include <algorithm>
+
+/**
+ * Class with utility methods shared across common object oriented languages.
+ * Specifically, most of this stuff is for C++/Java.
+ *
+ */
+class t_oop_generator : public t_generator {
+public:
+ t_oop_generator(t_program* program) : t_generator(program) {}
+
+ /**
+ * Scoping, using curly braces!
+ */
+
+ void scope_up(std::ostream& out) {
+ indent(out) << "{" << std::endl;
+ indent_up();
+ }
+
+ void scope_down(std::ostream& out) {
+ indent_down();
+ indent(out) << "}" << std::endl;
+ }
+
+ std::string upcase_string(std::string original) {
+ std::transform(original.begin(), original.end(), original.begin(), (int (*)(int))toupper);
+ return original;
+ }
+
+ virtual std::string get_enum_class_name(t_type* type) {
+ std::string package = "";
+ t_program* program = type->get_program();
+ if (program != NULL && program != program_) {
+ package = program->get_namespace("java") + ".";
+ }
+ return package + type->get_name();
+ }
+
+ virtual void generate_java_docstring_comment(std::ostream& out, std::string contents) {
+ generate_docstring_comment(out, "/**\n", " * ", contents, " */\n");
+ }
+
+ virtual void generate_java_doc(std::ostream& out, t_field* field) {
+ if (field->get_type()->is_enum()) {
+ std::string combined_message = field->get_doc() + "\n@see "
+ + get_enum_class_name(field->get_type());
+ generate_java_docstring_comment(out, combined_message);
+ } else {
+ generate_java_doc(out, (t_doc*)field);
+ }
+ }
+
+ /**
+ * Emits a JavaDoc comment if the provided object has a doc in Thrift
+ */
+ virtual void generate_java_doc(std::ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_java_docstring_comment(out, tdoc->get_doc());
+ }
+ }
+
+ /**
+ * Emits a JavaDoc comment if the provided function object has a doc in Thrift
+ */
+ virtual void generate_java_doc(std::ostream& out, t_function* tfunction) {
+ if (tfunction->has_doc()) {
+ std::stringstream ss;
+ ss << tfunction->get_doc();
+ const std::vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ std::vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ss << "\n@param " << p->get_name();
+ if (p->has_doc()) {
+ ss << " " << p->get_doc();
+ }
+ }
+ generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n");
+ }
+ }
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc
new file mode 100644
index 000000000..80729cbfd
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc
@@ -0,0 +1,1683 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <list>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * PERL code generator.
+ *
+ */
+class t_perl_generator : public t_oop_generator {
+public:
+ t_perl_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program), f_types_use_includes_emitted_(false) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option perl:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-perl";
+ escape_['$'] = "\\$";
+ escape_['@'] = "\\@";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ /**
+ * Structs!
+ */
+
+ void generate_perl_struct(t_struct* tstruct, bool is_exception);
+ void generate_perl_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false);
+ void generate_perl_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_perl_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_perl_function_helpers(t_function* tfunction);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_rest(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_processor(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+ void generate_use_includes(std::ostream& os, bool& done, t_type *type, bool selfish);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool inclass = false);
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string perl_includes();
+ std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+
+ std::string autogen_comment() override {
+ return std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "#\n";
+ }
+
+ void perl_namespace_dirs(t_program* p, std::list<std::string>& dirs) {
+ std::string ns = p->get_namespace("perl");
+ std::string::size_type loc;
+
+ if (ns.size() > 0) {
+ while ((loc = ns.find(".")) != std::string::npos) {
+ dirs.push_back(ns.substr(0, loc));
+ ns = ns.substr(loc + 1);
+ }
+ }
+
+ if (ns.size() > 0) {
+ dirs.push_back(ns);
+ }
+ }
+
+ std::string perl_namespace(t_program* p) {
+ std::string ns = p->get_namespace("perl");
+ std::string result = "";
+ std::string::size_type loc;
+
+ if (ns.size() > 0) {
+ while ((loc = ns.find(".")) != std::string::npos) {
+ result += ns.substr(0, loc);
+ result += "::";
+ ns = ns.substr(loc + 1);
+ }
+
+ if (ns.size() > 0) {
+ result += ns + "::";
+ }
+ }
+
+ return result;
+ }
+
+ std::string get_namespace_out_dir() {
+ std::string outdir = get_out_dir();
+ std::list<std::string> dirs;
+ perl_namespace_dirs(program_, dirs);
+ std::list<std::string>::iterator it;
+ for (it = dirs.begin(); it != dirs.end(); it++) {
+ outdir += *it + "/";
+ }
+ return outdir;
+ }
+
+private:
+ /**
+ * File streams
+ */
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_consts_;
+ ofstream_with_content_based_conditional_update f_helpers_;
+ ofstream_with_content_based_conditional_update f_service_;
+
+ bool f_types_use_includes_emitted_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_perl_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ string outdir = get_out_dir();
+ std::list<std::string> dirs;
+ perl_namespace_dirs(program_, dirs);
+ std::list<std::string>::iterator it;
+ for (it = dirs.begin(); it != dirs.end(); it++) {
+ outdir += *it + "/";
+ MKDIR(outdir.c_str());
+ }
+
+ // Make output file
+ string f_types_name = outdir + "Types.pm";
+ f_types_.open(f_types_name.c_str());
+ string f_consts_name = outdir + "Constants.pm";
+ f_consts_.open(f_consts_name.c_str());
+
+ // Print header
+ f_types_ << autogen_comment() << perl_includes();
+
+ // Print header
+ f_consts_ << autogen_comment() << "package " << perl_namespace(program_) << "Constants;" << endl
+ << perl_includes() << endl;
+}
+
+/**
+ * Prints standard java imports
+ */
+string t_perl_generator::perl_includes() {
+ string inc;
+
+ inc = "use 5.10.0;\n";
+ inc += "use strict;\n";
+ inc += "use warnings;\n";
+ inc += "use Thrift::Exception;\n";
+ inc += "use Thrift::MessageType;\n";
+ inc += "use Thrift::Type;\n\n";
+
+ return inc;
+}
+
+/**
+ * Close up (or down) some filez.
+ */
+void t_perl_generator::close_generator() {
+ // Close types file
+ f_types_ << "1;" << endl;
+ f_types_.close();
+
+ f_consts_ << "1;" << endl;
+ f_consts_.close();
+}
+
+/**
+ * Generates a typedef. This is not done in PERL, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_perl_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Generates code for an enumerated type. Since define is expensive to lookup
+ * in PERL, we use a global array for this.
+ *
+ * @param tenum The enumeration
+ */
+void t_perl_generator::generate_enum(t_enum* tenum) {
+ f_types_ << "package " << perl_namespace(program_) << tenum->get_name() << ";" << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ f_types_ << "use constant " << (*c_iter)->get_name() << " => " << value << ";" << endl;
+ }
+}
+
+/**
+ * Generate a constant value
+ */
+void t_perl_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ f_consts_ << "use constant " << name << " => ";
+ f_consts_ << render_const_value(type, value);
+ f_consts_ << ";" << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_perl_generator::render_const_value(t_type* type, t_const_value* value) {
+ std::ostringstream out;
+
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "1" : "0");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << perl_namespace(type->get_program()) << type->get_name() << "->new({" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ indent(out) << render_const_value(g_type_string, v_iter->first);
+ out << " => ";
+ out << render_const_value(field_type, v_iter->second);
+ out << ",";
+ out << endl;
+ }
+ indent_down();
+ indent(out) << "})";
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "{" << endl;
+ indent_up();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ indent(out) << render_const_value(ktype, v_iter->first);
+ out << " => ";
+ out << render_const_value(vtype, v_iter->second);
+ out << "," << endl;
+ }
+ indent_down();
+ indent(out) << "}";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ out << "[" << endl;
+ indent_up();
+
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+
+ indent(out) << render_const_value(etype, *v_iter);
+ if (type->is_set()) {
+ out << " => 1";
+ }
+ out << "," << endl;
+ }
+ indent_down();
+ indent(out) << "]";
+ }
+ return out.str();
+}
+
+/**
+ * Make a struct
+ */
+void t_perl_generator::generate_struct(t_struct* tstruct) {
+ generate_perl_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_perl_generator::generate_xception(t_struct* txception) {
+ generate_perl_struct(txception, true);
+}
+
+/**
+ * Structs can be normal or exceptions.
+ */
+void t_perl_generator::generate_perl_struct(t_struct* tstruct, bool is_exception) {
+ generate_use_includes(f_types_, f_types_use_includes_emitted_, tstruct, false);
+ generate_perl_struct_definition(f_types_, tstruct, is_exception);
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is nothing in PERL
+ * where the objects are all just associative arrays (unless of course we
+ * decide to start using objects for them...)
+ *
+ * @param tstruct The struct definition
+ */
+void t_perl_generator::generate_perl_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << "package " << perl_namespace(tstruct->get_program()) << tstruct->get_name() << ";\n";
+ if (is_exception) {
+ out << "use base qw(Thrift::TException);\n";
+ }
+
+ // Create simple acessor methods
+ out << "use base qw(Class::Accessor);\n";
+
+ if (members.size() > 0) {
+ out << perl_namespace(tstruct->get_program()) << tstruct->get_name() << "->mk_accessors( qw( ";
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if (!t->is_xception()) {
+ out << (*m_iter)->get_name() << " ";
+ }
+ }
+
+ out << ") );\n";
+ }
+
+ out << endl;
+
+ // new()
+ indent_up();
+ out << "sub new {" << endl << indent() << "my $classname = shift;" << endl << indent()
+ << "my $self = {};" << endl << indent() << "my $vals = shift || {};" << endl;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string dval = "undef";
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) {
+ dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
+ }
+ out << indent() << "$self->{" << (*m_iter)->get_name() << "} = " << dval << ";" << endl;
+ }
+
+ // Generate constructor from array
+ if (members.size() > 0) {
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) {
+ indent(out) << "$self->{" << (*m_iter)->get_name()
+ << "} = " << render_const_value(t, (*m_iter)->get_value()) << ";" << endl;
+ }
+ }
+
+ out << indent() << "if (UNIVERSAL::isa($vals,'HASH')) {" << endl;
+ indent_up();
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << indent() << "if (defined $vals->{" << (*m_iter)->get_name() << "}) {" << endl
+ << indent() << " $self->{" << (*m_iter)->get_name() << "} = $vals->{"
+ << (*m_iter)->get_name() << "};" << endl << indent() << "}" << endl;
+ }
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+
+ out << indent() << "return bless ($self, $classname);" << endl;
+ indent_down();
+ out << "}\n\n";
+
+ out << "sub getName {" << endl << indent() << " return '" << tstruct->get_name() << "';" << endl
+ << indent() << "}" << endl << endl;
+
+ generate_perl_struct_reader(out, tstruct);
+ generate_perl_struct_writer(out, tstruct);
+}
+
+/**
+ * Generates the read() method for a struct
+ */
+void t_perl_generator::generate_perl_struct_reader(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << "sub read {" << endl;
+
+ indent_up();
+
+ out << indent() << "my ($self, $input) = @_;" << endl << indent() << "my $xfer = 0;" << endl
+ << indent() << "my $fname;" << endl << indent() << "my $ftype = 0;" << endl << indent()
+ << "my $fid = 0;" << endl;
+
+ indent(out) << "$xfer += $input->readStructBegin(\\$fname);" << endl;
+
+ // Loop over reading in fields
+ indent(out) << "while (1)" << endl;
+
+ scope_up(out);
+
+ indent(out) << "$xfer += $input->readFieldBegin(\\$fname, \\$ftype, \\$fid);" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if ($ftype == Thrift::TType::STOP) {" << endl;
+ indent_up();
+ indent(out) << "last;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+
+ // Switch statement on the field we are reading
+ indent(out) << "SWITCH: for($fid)" << endl;
+
+ scope_up(out);
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+
+ indent(out) << "/^" << (*f_iter)->get_key() << "$/ && do{";
+ indent(out) << "if ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+
+ indent_up();
+ generate_deserialize_field(out, *f_iter, "self->");
+ indent_down();
+
+ indent(out) << "} else {" << endl;
+
+ indent(out) << " $xfer += $input->skip($ftype);" << endl;
+
+ out << indent() << "}" << endl << indent() << "last; };" << endl;
+ }
+ // In the default case we skip the field
+
+ indent(out) << " $xfer += $input->skip($ftype);" << endl;
+
+ scope_down(out);
+
+ indent(out) << "$xfer += $input->readFieldEnd();" << endl;
+
+ scope_down(out);
+
+ indent(out) << "$xfer += $input->readStructEnd();" << endl;
+
+ indent(out) << "return $xfer;" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates the write() method for a struct
+ */
+void t_perl_generator::generate_perl_struct_writer(ostream& out, t_struct* tstruct) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << "sub write {" << endl;
+
+ indent_up();
+ indent(out) << "my ($self, $output) = @_;" << endl;
+ indent(out) << "my $xfer = 0;" << endl;
+
+ indent(out) << "$xfer += $output->writeStructBegin('" << name << "');" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ out << indent() << "if (defined $self->{" << (*f_iter)->get_name() << "}) {" << endl;
+ indent_up();
+
+ indent(out) << "$xfer += $output->writeFieldBegin("
+ << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type())
+ << ", " << (*f_iter)->get_key() << ");" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "self->");
+
+ indent(out) << "$xfer += $output->writeFieldEnd();" << endl;
+
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ out << indent() << "$xfer += $output->writeFieldStop();" << endl << indent()
+ << "$xfer += $output->writeStructEnd();" << endl;
+
+ out << indent() << "return $xfer;" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+}
+
+/**
+ * Generates use clauses for included entities
+ *
+ * @param os The output stream
+ * @param done A flag reference to debounce the action
+ * @param type The type being processed
+ * @param selfish Flag to indicate if the current namespace types should be "use"d as well.
+ */
+void t_perl_generator::generate_use_includes(std::ostream& os, bool& done, t_type *type, bool selfish) {
+ t_program *current = type->get_program();
+ if (current && !done) {
+ std::vector<t_program*>& currInc = current->get_includes();
+ std::vector<t_program*>::size_type numInc = currInc.size();
+ if (selfish) {
+ os << "use " << perl_namespace(current) << "Types;" << endl;
+ }
+ for (std::vector<t_program*>::size_type i = 0; i < numInc; ++i) {
+ t_program* incProgram = currInc.at(i);
+ os << "use " << perl_namespace(incProgram) << "Types;" << endl;
+ }
+ os << endl;
+ done = true;
+ }
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_perl_generator::generate_service(t_service* tservice) {
+ string f_service_name = get_namespace_out_dir() + service_name_ + ".pm";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << autogen_comment() << perl_includes();
+
+ bool done = false;
+ generate_use_includes(f_service_, done, tservice, true);
+
+ t_service* extends_s = tservice->get_extends();
+ if (extends_s != NULL) {
+ f_service_ << "use " << perl_namespace(extends_s->get_program()) << extends_s->get_name() << ";"
+ << endl;
+ }
+
+ f_service_ << endl;
+
+ // Generate the three main parts of the service (well, two for now in PERL)
+ generate_service_helpers(tservice);
+ generate_service_interface(tservice);
+ generate_service_rest(tservice);
+ generate_service_client(tservice);
+ generate_service_processor(tservice);
+
+ // Close service file
+ f_service_ << "1;" << endl;
+ f_service_.close();
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_perl_generator::generate_service_processor(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ t_service* extends_s = tservice->get_extends();
+ if (extends_s != NULL) {
+ extends = perl_namespace(extends_s->get_program()) + extends_s->get_name();
+ extends_processor = "use base qw(" + extends + "Processor);";
+ }
+
+ indent_up();
+
+ // Generate the header portion
+ f_service_ << "package " << perl_namespace(program_) << service_name_ << "Processor;" << endl
+ << endl << "use strict;" << endl << extends_processor << endl << endl;
+
+ if (extends.empty()) {
+ f_service_ << "sub new {" << endl;
+
+ indent_up();
+
+ f_service_ << indent() << "my ($classname, $handler) = @_;" << endl << indent()
+ << "my $self = {};" << endl;
+
+ f_service_ << indent() << "$self->{handler} = $handler;" << endl;
+
+ f_service_ << indent() << "return bless ($self, $classname);" << endl;
+
+ indent_down();
+
+ f_service_ << "}" << endl << endl;
+ }
+
+ // Generate the server implementation
+ f_service_ << "sub process {" << endl;
+ indent_up();
+
+ f_service_ << indent() << "my ($self, $input, $output) = @_;" << endl;
+
+ f_service_ << indent() << "my $rseqid = 0;" << endl << indent() << "my $fname = undef;" << endl
+ << indent() << "my $mtype = 0;" << endl << endl;
+
+ f_service_ << indent() << "$input->readMessageBegin(\\$fname, \\$mtype, \\$rseqid);" << endl;
+
+ // HOT: check for method implementation
+ f_service_ << indent() << "my $methodname = 'process_'.$fname;" << endl << indent()
+ << "if (!$self->can($methodname)) {" << endl;
+ indent_up();
+
+ f_service_ << indent() << "$input->skip(Thrift::TType::STRUCT);" << endl << indent()
+ << "$input->readMessageEnd();" << endl << indent()
+ << "my $x = Thrift::TApplicationException->new('Function '.$fname.' not implemented.', "
+ "Thrift::TApplicationException::UNKNOWN_METHOD);" << endl << indent()
+ << "$output->writeMessageBegin($fname, Thrift::TMessageType::EXCEPTION, $rseqid);" << endl
+ << indent() << "$x->write($output);" << endl << indent()
+ << "$output->writeMessageEnd();" << endl << indent()
+ << "$output->getTransport()->flush();" << endl << indent() << "return;" << endl;
+
+ indent_down();
+ f_service_ << indent() << "}" << endl << indent()
+ << "$self->$methodname($rseqid, $input, $output);" << endl << indent() << "return 1;"
+ << endl;
+
+ indent_down();
+
+ f_service_ << "}" << endl << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_perl_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ // Open function
+ f_service_ << "sub process_" << tfunction->get_name() << " {" << endl;
+
+ indent_up();
+
+ f_service_ << indent() << "my ($self, $seqid, $input, $output) = @_;" << endl;
+
+ string argsname = perl_namespace(tservice->get_program()) + service_name_ + "_"
+ + tfunction->get_name() + "_args";
+ string resultname = perl_namespace(tservice->get_program()) + service_name_ + "_"
+ + tfunction->get_name() + "_result";
+
+ f_service_ << indent() << "my $args = " << argsname << "->new();" << endl << indent()
+ << "$args->read($input);" << endl;
+
+ f_service_ << indent() << "$input->readMessageEnd();" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "my $result = " << resultname << "->new();" << endl;
+ }
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ f_service_ << indent() << "eval {" << endl;
+ indent_up();
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "$result->{success} = ";
+ }
+ f_service_ << "$self->{handler}->" << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "$args->" << (*f_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ indent_down();
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "}; if( UNIVERSAL::isa($@,'"
+ << perl_namespace((*x_iter)->get_type()->get_program())
+ << (*x_iter)->get_type()->get_name() << "') ){ " << endl;
+
+ indent_up();
+ f_service_ << indent() << "$result->{" << (*x_iter)->get_name() << "} = $@;" << endl;
+ f_service_ << indent() << "$@ = undef;" << endl;
+ indent_down();
+ f_service_ << indent();
+ }
+ f_service_ << "}" << endl;
+
+ // catch-all for unexpected exceptions (THRIFT-3191)
+ f_service_ << indent() << "if ($@) {" << endl;
+ indent_up();
+ f_service_ << indent() << "$@ =~ s/^\\s+|\\s+$//g;" << endl
+ << indent() << "my $err = Thrift::TApplicationException->new(\"Unexpected Exception: \" . $@, Thrift::TApplicationException::INTERNAL_ERROR);" << endl
+ << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', Thrift::TMessageType::EXCEPTION, $seqid);" << endl
+ << indent() << "$err->write($output);" << endl
+ << indent() << "$output->writeMessageEnd();" << endl
+ << indent() << "$output->getTransport()->flush();" << endl
+ << indent() << "$@ = undef;" << endl
+ << indent() << "return;" << endl;
+ indent_down();
+ f_service_ << indent() << "}" << endl;
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "return;" << endl;
+ indent_down();
+ f_service_ << "}" << endl;
+ return;
+ }
+
+ // Serialize the reply
+ f_service_ << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', Thrift::TMessageType::REPLY, $seqid);" << endl
+ << indent() << "$result->write($output);" << endl
+ << indent() << "$output->writeMessageEnd();" << endl
+ << indent() << "$output->getTransport()->flush();" << endl;
+
+ // Close function
+ indent_down();
+ f_service_ << "}" << endl << endl;
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_perl_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ f_service_ << "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ string name = ts->get_name();
+ ts->set_name(service_name_ + "_" + name);
+ generate_perl_struct_definition(f_service_, ts, false);
+ generate_perl_function_helpers(*f_iter);
+ ts->set_name(name);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_perl_generator::generate_perl_function_helpers(t_function* tfunction) {
+ t_struct result(program_, service_name_ + "_" + tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ generate_perl_struct_definition(f_service_, &result, false);
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_perl_generator::generate_service_interface(t_service* tservice) {
+ string extends_if = "";
+ t_service* extends_s = tservice->get_extends();
+ if (extends_s != NULL) {
+ extends_if = "use base qw(" + perl_namespace(extends_s->get_program()) + extends_s->get_name()
+ + "If);";
+ }
+
+ f_service_ << "package " << perl_namespace(program_) << service_name_ << "If;" << endl << endl
+ << "use strict;" << endl << extends_if << endl << endl;
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << "sub " << function_signature(*f_iter) << endl << " die 'implement interface';\n}"
+ << endl << endl;
+ }
+ indent_down();
+}
+
+/**
+ * Generates a REST interface
+ */
+void t_perl_generator::generate_service_rest(t_service* tservice) {
+ string extends = "";
+ string extends_if = "";
+ t_service* extends_s = tservice->get_extends();
+ if (extends_s != NULL) {
+ extends = extends_s->get_name();
+ extends_if = "use base qw(" + perl_namespace(extends_s->get_program()) + extends_s->get_name()
+ + "Rest);";
+ }
+ f_service_ << "package " << perl_namespace(program_) << service_name_ << "Rest;" << endl << endl
+ << "use strict;" << endl << extends_if << endl << endl;
+
+ if (extends.empty()) {
+ f_service_ << "sub new {" << endl;
+
+ indent_up();
+
+ f_service_ << indent() << "my ($classname, $impl) = @_;" << endl << indent()
+ << "my $self ={ impl => $impl };" << endl << endl << indent()
+ << "return bless($self,$classname);" << endl;
+
+ indent_down();
+
+ f_service_ << "}" << endl << endl;
+ }
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << "sub " << (*f_iter)->get_name() << "{" << endl;
+
+ indent_up();
+
+ f_service_ << indent() << "my ($self, $request) = @_;" << endl << endl;
+
+ const vector<t_field*>& args = (*f_iter)->get_arglist()->get_members();
+ vector<t_field*>::const_iterator a_iter;
+ for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
+ t_type* atype = get_true_type((*a_iter)->get_type());
+ string req = "$request->{'" + (*a_iter)->get_name() + "'}";
+ f_service_ << indent() << "my $" << (*a_iter)->get_name() << " = (" << req << ") ? " << req
+ << " : undef;" << endl;
+ if (atype->is_string() && ((t_base_type*)atype)->is_string_list()) {
+ f_service_ << indent() << "my @" << (*a_iter)->get_name() << " = split(/,/, $"
+ << (*a_iter)->get_name() << ");" << endl << indent() << "$"
+ << (*a_iter)->get_name() << " = \\@" << (*a_iter)->get_name() << endl;
+ }
+ }
+ f_service_ << indent() << "return $self->{impl}->" << (*f_iter)->get_name() << "("
+ << argument_list((*f_iter)->get_arglist()) << ");" << endl;
+ indent_down();
+ indent(f_service_) << "}" << endl << endl;
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_perl_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ t_service* extends_s = tservice->get_extends();
+ if (extends_s != NULL) {
+ extends = perl_namespace(extends_s->get_program()) + extends_s->get_name();
+ extends_client = "use base qw(" + extends + "Client);";
+ }
+
+ f_service_ << "package " << perl_namespace(program_) << service_name_ << "Client;" << endl << endl
+ << extends_client << endl << "use base qw(" << perl_namespace(program_)
+ << service_name_ << "If);" << endl;
+
+ // Constructor function
+ f_service_ << "sub new {" << endl;
+
+ indent_up();
+
+ f_service_ << indent() << "my ($classname, $input, $output) = @_;" << endl << indent()
+ << "my $self = {};" << endl;
+
+ if (!extends.empty()) {
+ f_service_ << indent() << "$self = $classname->SUPER::new($input, $output);" << endl;
+ } else {
+ f_service_ << indent() << "$self->{input} = $input;" << endl << indent()
+ << "$self->{output} = defined $output ? $output : $input;" << endl << indent()
+ << "$self->{seqid} = 0;" << endl;
+ }
+
+ f_service_ << indent() << "return bless($self,$classname);" << endl;
+
+ indent_down();
+
+ f_service_ << "}" << endl << endl;
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ f_service_ << "sub " << function_signature(*f_iter) << endl;
+
+ indent_up();
+
+ indent(f_service_) << indent() << "$self->send_" << funname << "(";
+
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "$" << (*fld_iter)->get_name();
+ }
+ f_service_ << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "$self->recv_" << funname << "();" << endl;
+ }
+
+ indent_down();
+
+ f_service_ << "}" << endl << endl;
+
+ f_service_ << "sub send_" << function_signature(*f_iter) << endl;
+
+ indent_up();
+
+ std::string argsname = perl_namespace(tservice->get_program()) + service_name_ + "_"
+ + (*f_iter)->get_name() + "_args";
+
+ // Serialize the request header
+ f_service_ << indent() << "$self->{output}->writeMessageBegin('" << (*f_iter)->get_name()
+ << "', " << ((*f_iter)->is_oneway() ? "Thrift::TMessageType::ONEWAY" : "Thrift::TMessageType::CALL")
+ << ", $self->{seqid});" << endl;
+
+ f_service_ << indent() << "my $args = " << argsname << "->new();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "$args->{" << (*fld_iter)->get_name() << "} = $"
+ << (*fld_iter)->get_name() << ";" << endl;
+ }
+
+ // Write to the stream
+ f_service_ << indent() << "$args->write($self->{output});" << endl << indent()
+ << "$self->{output}->writeMessageEnd();" << endl << indent()
+ << "$self->{output}->getTransport()->flush();" << endl;
+
+ indent_down();
+
+ f_service_ << "}" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultname = perl_namespace(tservice->get_program()) + service_name_ + "_"
+ + (*f_iter)->get_name() + "_result";
+ t_struct noargs(program_);
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ // Open function
+ f_service_ << endl << "sub " << function_signature(&recv_function) << endl;
+
+ indent_up();
+
+ f_service_ << indent() << "my $rseqid = 0;" << endl << indent() << "my $fname;" << endl
+ << indent() << "my $mtype = 0;" << endl << endl;
+
+ f_service_ << indent() << "$self->{input}->readMessageBegin(\\$fname, \\$mtype, \\$rseqid);"
+ << endl << indent() << "if ($mtype == Thrift::TMessageType::EXCEPTION) {" << endl
+ << indent() << " my $x = Thrift::TApplicationException->new();" << endl << indent()
+ << " $x->read($self->{input});" << endl << indent()
+ << " $self->{input}->readMessageEnd();" << endl << indent() << " die $x;" << endl
+ << indent() << "}" << endl;
+
+ f_service_ << indent() << "my $result = " << resultname << "->new();" << endl << indent()
+ << "$result->read($self->{input});" << endl;
+
+ f_service_ << indent() << "$self->{input}->readMessageEnd();" << endl << endl;
+
+ // Careful, only return result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if (defined $result->{success} ) {" << endl << indent()
+ << " return $result->{success};" << endl << indent() << "}" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_ << indent() << "if (defined $result->{" << (*x_iter)->get_name() << "}) {"
+ << endl << indent() << " die $result->{" << (*x_iter)->get_name() << "};"
+ << endl << indent() << "}" << endl;
+ }
+
+ // Careful, only return _result if not a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_) << "return;" << endl;
+ } else {
+ f_service_ << indent() << "die \"" << (*f_iter)->get_name() << " failed: unknown result\";"
+ << endl;
+ }
+
+ // Close function
+ indent_down();
+ f_service_ << "}" << endl;
+ }
+ }
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_perl_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool inclass) {
+ (void)inclass;
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = tfield->get_name();
+
+ // Hack for when prefix is defined (always a hash ref)
+ if (!prefix.empty()) {
+ name = prefix + "{" + tfield->get_name() + "}";
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << "$xfer += $input->";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "readString(\\$" << name << ");";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool(\\$" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte(\\$" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16(\\$" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32(\\$" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64(\\$" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble(\\$" << name << ");";
+ break;
+ default:
+ throw "compiler error: no PERL name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32(\\$" << name << ");";
+ }
+ out << endl;
+
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a variable. This makes two key assumptions,
+ * first that there is a const char* variable named data that points to the
+ * buffer for deserialization, and that there is a variable protocol which
+ * is a reference to a TProtocol serialization object.
+ */
+void t_perl_generator::generate_deserialize_struct(ostream& out,
+ t_struct* tstruct,
+ string prefix) {
+ out << indent() << "$" << prefix << " = " << perl_namespace(tstruct->get_program())
+ << tstruct->get_name() << "->new();" << endl << indent() << "$xfer += $" << prefix
+ << "->read($input);" << endl;
+}
+
+void t_perl_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+
+ t_field fsize(g_type_i32, size);
+ t_field fktype(g_type_i8, ktype);
+ t_field fvtype(g_type_i8, vtype);
+ t_field fetype(g_type_i8, etype);
+
+ out << indent() << "my $" << size << " = 0;" << endl;
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ out << indent() << "$" << prefix << " = {};" << endl << indent() << "my $" << ktype << " = 0;"
+ << endl << indent() << "my $" << vtype << " = 0;" << endl;
+
+ out << indent() << "$xfer += $input->readMapBegin("
+ << "\\$" << ktype << ", \\$" << vtype << ", \\$" << size << ");" << endl;
+
+ } else if (ttype->is_set()) {
+
+ out << indent() << "$" << prefix << " = {};" << endl << indent() << "my $" << etype << " = 0;"
+ << endl << indent() << "$xfer += $input->readSetBegin("
+ << "\\$" << etype << ", \\$" << size << ");" << endl;
+
+ } else if (ttype->is_list()) {
+
+ out << indent() << "$" << prefix << " = [];" << endl << indent() << "my $" << etype << " = 0;"
+ << endl << indent() << "$xfer += $input->readListBegin("
+ << "\\$" << etype << ", \\$" << size << ");" << endl;
+ }
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for (my $" << i << " = 0; $" << i << " < $" << size << "; ++$" << i << ")"
+ << endl;
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "$xfer += $input->readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "$xfer += $input->readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "$xfer += $input->readListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_perl_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("key");
+ string val = tmp("val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey, true, true) << endl;
+ indent(out) << declare_field(&fval, true, true) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << "$" << prefix << "->{$" << key << "} = $" << val << ";" << endl;
+}
+
+void t_perl_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << "my $" << elem << " = undef;" << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << "$" << prefix << "->{$" << elem << "} = 1;" << endl;
+}
+
+void t_perl_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << "my $" << elem << " = undef;" << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << "push(@{$" << prefix << "},$" << elem << ");" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_perl_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + "{" + tfield->get_name() + "}");
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + "{" + tfield->get_name() + "}");
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = tfield->get_name();
+
+ // Hack for when prefix is defined (always a hash ref)
+ if (!prefix.empty())
+ name = prefix + "{" + tfield->get_name() + "}";
+
+ indent(out) << "$xfer += $output->";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "writeString($" << name << ");";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool($" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte($" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16($" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32($" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64($" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble($" << name << ");";
+ break;
+ default:
+ throw "compiler error: no PERL name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32($" << name << ");";
+ }
+ out << endl;
+
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_perl_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << "$xfer += $" << prefix << "->write($output);" << endl;
+}
+
+/**
+ * Writes out a container
+ */
+void t_perl_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "$xfer += $output->writeMapBegin("
+ << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "scalar(keys %{$" << prefix << "}));" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "$xfer += $output->writeSetBegin("
+ << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
+ << "scalar(@{$" << prefix << "}));" << endl;
+
+ } else if (ttype->is_list()) {
+
+ indent(out) << "$xfer += $output->writeListBegin("
+ << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", "
+ << "scalar(@{$" << prefix << "}));" << endl;
+ }
+
+ scope_up(out);
+
+ if (ttype->is_map()) {
+ string kiter = tmp("kiter");
+ string viter = tmp("viter");
+ indent(out) << "while( my ($" << kiter << ",$" << viter << ") = each %{$" << prefix << "}) "
+ << endl;
+
+ scope_up(out);
+ generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+ scope_down(out);
+
+ } else if (ttype->is_set()) {
+ string iter = tmp("iter");
+ indent(out) << "foreach my $" << iter << " (@{$" << prefix << "})" << endl;
+ scope_up(out);
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ scope_down(out);
+
+ } else if (ttype->is_list()) {
+ string iter = tmp("iter");
+ indent(out) << "foreach my $" << iter << " (@{$" << prefix << "}) " << endl;
+ scope_up(out);
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ scope_down(out);
+ }
+
+ scope_down(out);
+
+ if (ttype->is_map()) {
+ indent(out) << "$xfer += $output->writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "$xfer += $output->writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "$xfer += $output->writeListEnd();" << endl;
+ }
+
+ scope_down(out);
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_perl_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), kiter);
+ generate_serialize_field(out, &kfield);
+
+ t_field vfield(tmap->get_val_type(), viter);
+ generate_serialize_field(out, &vfield);
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_perl_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield);
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_perl_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield);
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_perl_generator::declare_field(t_field* tfield, bool init, bool obj) {
+ string result = "my $" + tfield->get_name();
+ if (init) {
+ t_type* type = get_true_type(tfield->get_type());
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ break;
+ case t_base_type::TYPE_STRING:
+ result += " = ''";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = 0.0";
+ break;
+ default:
+ throw "compiler error: no PERL initializer for base type "
+ + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ result += " = 0";
+ } else if (type->is_container()) {
+ result += " = []";
+ } else if (type->is_struct() || type->is_xception()) {
+ if (obj) {
+ result += " = " + perl_namespace(type->get_program()) + type->get_name() + "->new()";
+ } else {
+ result += " = undef";
+ }
+ }
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_perl_generator::function_signature(t_function* tfunction, string prefix) {
+
+ string str;
+
+ str = prefix + tfunction->get_name() + "{\n";
+ str += " my $self = shift;\n";
+
+ // Need to create perl function arg inputs
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ str += " my $" + (*f_iter)->get_name() + " = shift;\n";
+ }
+
+ return str;
+}
+
+/**
+ * Renders a field list
+ */
+string t_perl_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += "$" + (*f_iter)->get_name();
+ }
+ return result;
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_perl_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "Thrift::TType::STRING";
+ case t_base_type::TYPE_BOOL:
+ return "Thrift::TType::BOOL";
+ case t_base_type::TYPE_I8:
+ return "Thrift::TType::BYTE";
+ case t_base_type::TYPE_I16:
+ return "Thrift::TType::I16";
+ case t_base_type::TYPE_I32:
+ return "Thrift::TType::I32";
+ case t_base_type::TYPE_I64:
+ return "Thrift::TType::I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "Thrift::TType::DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "Thrift::TType::I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "Thrift::TType::STRUCT";
+ } else if (type->is_map()) {
+ return "Thrift::TType::MAP";
+ } else if (type->is_set()) {
+ return "Thrift::TType::SET";
+ } else if (type->is_list()) {
+ return "Thrift::TType::LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(perl, "Perl", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc
new file mode 100644
index 000000000..79bd5a283
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc
@@ -0,0 +1,2785 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+#define NSGLOBAL (nsglobal_.size() ? nsglobal_ : "")
+#define NSGLOBAL_A ("\\" + NSGLOBAL)
+#define NSGLOBAL_B (NSGLOBAL + "\\")
+#define NSGLOBAL_AB ("\\" + NSGLOBAL + "\\")
+
+/**
+ * PHP code generator.
+ *
+ */
+class t_php_generator : public t_oop_generator {
+public:
+ t_php_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ binary_inline_ = false;
+ rest_ = false;
+ phps_ = false;
+ oop_ = false;
+ validate_ = false;
+ json_serializable_ = false;
+ nsglobal_ = ""; // by default global namespace is empty
+ classmap_ = false;
+ for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if (iter->first.compare("inlined") == 0) {
+ binary_inline_ = true;
+ } else if (iter->first.compare("rest") == 0) {
+ rest_ = true;
+ } else if (iter->first.compare("server") == 0) {
+ phps_ = true;
+ } else if (iter->first.compare("oop") == 0) {
+ oop_ = true;
+ } else if (iter->first.compare("validate") == 0) {
+ validate_ = true;
+ } else if (iter->first.compare("json") == 0) {
+ json_serializable_ = true;
+ } else if (iter->first.compare("nsglobal") == 0) {
+ nsglobal_ = iter->second;
+ } else if (iter->first.compare("classmap") == 0) {
+ classmap_ = true;
+ } else if (iter->first.compare("psr4") == 0) {
+ if(classmap_){
+ throw "psr4 and classmap are mutually exclusive.";
+ } else {
+ pwarning(0, "psr4 is default option! needn't add psr4 option!\n");
+ }
+ } else {
+ throw "unknown option php:" + iter->first;
+ }
+ }
+
+ if (oop_ && binary_inline_) {
+ throw "oop and inlined are mutually exclusive.";
+ }
+
+ out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php");
+ escape_['$'] = "\\$";
+ }
+
+ std::string indent_str() const override {
+ return " ";
+ }
+
+ static bool is_valid_namespace(const std::string& sub_namespace);
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_consts(vector<t_const*> consts) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ /**
+ * Structs!
+ */
+
+ void generate_php_struct(t_struct* tstruct, bool is_exception);
+ void generate_php_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false,
+ bool is_result = false);
+ void generate_php_struct_reader(std::ostream& out, t_struct* tstruct, bool is_result);
+ void generate_php_struct_writer(std::ostream& out, t_struct* tstruct, bool is_result);
+ void generate_php_function_helpers(t_service* tservice, t_function* tfunction);
+ void generate_php_struct_required_validator(ostream& out,
+ t_struct* tstruct,
+ std::string method_name,
+ bool write_mode);
+ void generate_php_struct_read_validator(ostream& out, t_struct* tstruct);
+ void generate_php_struct_write_validator(ostream& out, t_struct* tstruct);
+ void generate_php_struct_json_serialize(ostream& out, t_struct* tstruct, bool is_result);
+ bool needs_php_write_validator(t_struct* tstruct, bool is_result);
+ bool needs_php_read_validator(t_struct* tstruct, bool is_result);
+ int get_php_num_required_fields(const vector<t_field*>& fields, bool write_mode);
+
+ void generate_php_type_spec(std::ostream& out, t_type* t);
+ void generate_php_struct_spec(std::ostream& out, t_struct* tstruct);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_rest(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_processor(t_service* tservice);
+ void generate_process_function(std::ostream& out, t_service* tservice, t_function* tfunction);
+ void generate_service_header(t_service* tservice, std::ostream& file);
+ void generate_program_header(std::ostream& file);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool inclass = false);
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_php_doc(std::ostream& out, t_doc* tdoc);
+
+ void generate_php_doc(std::ostream& out, t_field* tfield);
+
+ void generate_php_doc(std::ostream& out, t_function* tfunction);
+
+ void generate_php_docstring_comment(std::ostream& out, string contents);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string php_includes();
+ std::string declare_field(t_field* tfield, bool init = false, bool obj = false);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct, bool addTypeHints = true);
+ std::string type_to_cast(t_type* ttype);
+ std::string type_to_enum(t_type* ttype);
+ std::string type_to_phpdoc(t_type* ttype);
+
+ bool php_is_scalar(t_type *ttype) {
+ ttype = ttype->get_true_type();
+ if(ttype->is_base_type()) {
+ return true;
+ } else if(ttype->is_enum()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ std::string php_namespace_base(const t_program* p) {
+ std::string ns = p->get_namespace("php");
+ const char* delimiter = "\\";
+ size_t position = ns.find('.');
+ while (position != string::npos) {
+ ns.replace(position, 1, delimiter);
+ position = ns.find('.', position + 1);
+ }
+ return ns;
+ }
+
+ // general use namespace prefixing: \my\namespace\ or my_namespace_
+ string php_namespace(const t_program* p) {
+ string ns = php_namespace_base(p);
+ return (nsglobal_.size() ? NSGLOBAL_AB : NSGLOBAL_B) + (ns.size() ? (ns + "\\") : "");
+ }
+
+ // return the namespace of a file:
+ // global\ns\sub\ns or global\ns or sub\ns
+ string php_namespace_suffix(const t_program* p) {
+ string ns = php_namespace_base(p);
+
+ return NSGLOBAL
+ + (ns.size() && NSGLOBAL.size() ? "\\" : "")
+ + ns;
+ }
+
+ // add a directory to already existing namespace
+ string php_namespace_directory(string directory, bool end = true) {
+ (void)directory;
+ if (end) {
+ return ";";
+ } else {
+ return "";
+ }
+ }
+
+ // writing an autload identifier into globa;ls: my\namespace\ or my_namespace_
+ string php_namespace_autoload(const t_program* p) {
+ std::string ns = php_namespace_base(p);
+ return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + (ns.size() ? (ns + "\\") : "");
+ }
+
+ // declaring a type: typename or my_namespace_typename
+ string php_namespace_declaration(t_type* t) { return t->get_name(); }
+
+ std::string php_path(t_program* p) {
+ std::string ns = p->get_namespace("php.path");
+ if (ns.empty()) {
+ return p->get_name();
+ }
+
+ // Transform the java-style namespace into a path.
+ for (char & n : ns) {
+ if (n == '.') {
+ n = '/';
+ }
+ }
+
+ return ns + '/';
+ }
+
+ /**
+ * Transform class_method into ClassMethod
+ *
+ * @param str
+ * @return stirng
+ */
+ string classify(string str) {
+ string classe = "";
+
+ vector<string> x = split(str, '_');
+
+ for (const auto & i : x) {
+ classe = classe + capitalize(i);
+ }
+
+ return classe;
+ }
+
+ /**
+ * Split method
+ * @param s
+ * @param delim
+ * @param elems
+ * @return
+ */
+ vector<string>& split(const string& s, char delim, vector<string>& elems) {
+ stringstream ss(s);
+ string item;
+
+ while (getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+
+ return elems;
+ }
+
+ vector<string> split(const string& s, char delim) {
+ vector<string> elems;
+
+ return split(s, delim, elems);
+ }
+
+ /**
+ * Capitalize method
+ * @param str
+ * @return
+ */
+ string capitalize(string str) {
+ string::iterator it(str.begin());
+
+ if (it != str.end())
+ str[0] = toupper((unsigned char)str[0]);
+
+ // while(++it != str.end())
+ // {
+ // *it = tolower((unsigned char)*it);
+ // }
+ return str;
+ }
+
+private:
+ /**
+ * File streams
+ */
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_service_;
+
+ std::string package_dir_;
+ /**
+ * Generate protocol-independent template? Or Binary inline code?
+ */
+ bool binary_inline_;
+
+ /**
+ * Generate a REST handler class
+ */
+ bool rest_;
+
+ /**
+ * Generate stubs for a PHP server
+ */
+ bool phps_;
+
+ /**
+ * Whether to use OOP base class TBase
+ */
+ bool oop_;
+
+ /**
+ * Whether to generate old-style PHP file to use classmap autoloading
+ */
+ bool classmap_;
+
+ /**
+ * Whether to generate validator code
+ */
+ bool validate_;
+
+ /**
+ * Whether to generate JsonSerializable classes
+ */
+ bool json_serializable_;
+
+ /**
+ * Global namespace for PHP 5.3
+ */
+ std::string nsglobal_;
+};
+
+bool t_php_generator::is_valid_namespace(const std::string& sub_namespace) {
+ return sub_namespace == "path";
+}
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_php_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ // Create Real directory Namespaces
+ vector<string> NSx = split(php_namespace_suffix(get_program()), '\\');
+ package_dir_ = get_out_dir();
+
+ for (const auto & i : NSx) {
+ package_dir_ = package_dir_ + "/" + i + "/";
+ MKDIR(package_dir_.c_str());
+ }
+
+ // Prepare output file for all the types in classmap mode
+ if (classmap_) {
+ // Make output file
+ string f_types_name = package_dir_ + "Types.php";
+ f_types_.open(f_types_name.c_str());
+ generate_program_header(f_types_);
+ }
+}
+
+/**
+ * Prints standard php includes
+ */
+string t_php_generator::php_includes() {
+ string includes = "use Thrift\\Base\\TBase;\n"
+ "use Thrift\\Type\\TType;\n"
+ "use Thrift\\Type\\TMessageType;\n"
+ "use Thrift\\Exception\\TException;\n"
+ "use Thrift\\Exception\\TProtocolException;\n"
+ "use Thrift\\Protocol\\TProtocol;\n"
+ "use Thrift\\Protocol\\TBinaryProtocolAccelerated;\n"
+ "use Thrift\\Exception\\TApplicationException;\n";
+
+ if (json_serializable_) {
+ includes += "use JsonSerializable;\n"
+ "use stdClass;\n";
+ }
+
+ return includes;
+}
+
+/**
+ * Close up (or down) some filez.
+ */
+void t_php_generator::close_generator() {
+ if (classmap_) {
+ // Close types file
+ f_types_.close();
+ }
+}
+
+/**
+ * Generates a typedef. This is not done in PHP, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_php_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Generates service header contains namespace suffix and includes inside file specified
+ */
+void t_php_generator::generate_service_header(t_service* tservice, std::ostream& file) {
+ file << "<?php" << endl;
+ if (!php_namespace_suffix(tservice->get_program()).empty()) {
+ file << "namespace " << php_namespace_suffix(tservice->get_program()) << ";" << endl
+ << endl;
+ }
+ file << autogen_comment() << php_includes();
+
+ file << endl;
+}
+
+/**
+ * Generates program header contains namespace suffix and includes inside file specified
+ */
+void t_php_generator::generate_program_header(std::ostream& file) {
+ file << "<?php" << endl;
+ if (!php_namespace_suffix(get_program()).empty()) {
+ file << "namespace " << php_namespace_suffix(get_program()) << ";" << endl
+ << endl;
+ }
+ file << autogen_comment() << php_includes();
+
+ file << endl;
+}
+
+/**
+ * Generates code for an enumerated type. Since define is expensive to lookup
+ * in PHP, we use a global array for this.
+ *
+ * @param tenum The enumeration
+ */
+void t_php_generator::generate_enum(t_enum* tenum) {
+ ofstream_with_content_based_conditional_update& f_enum = f_types_;
+ if (!classmap_) {
+ string f_enum_name = package_dir_ + tenum->get_name() + ".php";
+ f_enum.open(f_enum_name.c_str());
+ generate_program_header(f_enum);
+ }
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ // We're also doing it this way to see how it performs. It's more legible
+ // code but you can't do things like an 'extract' on it, which is a bit of
+ // a downer.
+ generate_php_doc(f_enum, tenum);
+ f_enum << "final class " << tenum->get_name() << endl
+ << "{" << endl;
+ indent_up();
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ generate_php_doc(f_enum, *c_iter);
+ indent(f_enum) << "const " << (*c_iter)->get_name() << " = " << value << ";" << endl
+ << endl;
+ }
+
+ indent(f_enum) << "static public $__names = array(" << endl;
+
+ indent_up();
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_enum) << value << " => '" << (*c_iter)->get_name() << "'," << endl;
+ }
+ indent_down();
+ indent(f_enum) << ");" << endl;
+
+ indent_down();
+
+ f_enum << "}" << endl << endl;
+ if (!classmap_) {
+ f_enum.close();
+ }
+}
+
+/**
+ * Generate constant class
+ *
+ * Override the one from t_generator
+ */
+void t_php_generator::generate_consts(vector<t_const*> consts) {
+ vector<t_const*>::iterator c_iter;
+
+ // Create class only if needed
+ if (consts.size() > 0) {
+
+ ofstream_with_content_based_conditional_update& f_consts = f_types_;
+ if (!classmap_) {
+ string f_consts_name = package_dir_ + "Constant.php";
+ f_consts.open(f_consts_name.c_str());
+ generate_program_header(f_consts);
+ }
+ f_consts << "final class Constant extends \\Thrift\\Type\\TConstant"<< endl
+ << "{" << endl;
+
+ indent_up();
+
+ // Create static property
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ string name = (*c_iter)->get_name();
+
+ indent(f_consts) << "static protected $" << name << ";" << endl;
+ }
+
+ // Create init function
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ string name = (*c_iter)->get_name();
+
+ f_consts << endl;
+
+ f_consts << indent() << "protected static function init_" << name << "()" <<endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ indent(f_consts) << "return ";
+ generate_php_doc(f_consts, *c_iter);
+ f_consts << render_const_value((*c_iter)->get_type(), (*c_iter)->get_value());
+ f_consts << ";" << endl;
+
+ indent_down();
+ indent(f_consts) << "}" << endl;
+ }
+
+ indent_down();
+ f_consts << "}" << endl;
+ if (!classmap_) {
+ f_consts.close();
+ }
+ }
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_php_generator::render_const_value(t_type* type, t_const_value* value) {
+ std::ostringstream out;
+ type = get_true_type(type);
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ indent(out) << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << "new " << php_namespace(type->get_program()) << type->get_name() << "(array(" << endl;
+ indent_up();
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ out << indent();
+ out << render_const_value(g_type_string, v_iter->first);
+ out << " => ";
+ out << render_const_value(field_type, v_iter->second);
+ out << "," << endl;
+ }
+ indent_down();
+ indent(out) << "))";
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "array(" << endl;
+ indent_up();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent();
+ out << render_const_value(ktype, v_iter->first);
+ out << " => ";
+ out << render_const_value(vtype, v_iter->second);
+ out << "," << endl;
+ }
+ indent_down();
+ indent(out) << ")";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ out << "array(" << endl;
+ indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent();
+ out << render_const_value(etype, *v_iter);
+ if (type->is_set()) {
+ out << " => true";
+ }
+ out << "," << endl;
+ }
+ indent_down();
+ indent(out) << ")";
+ }
+ return out.str();
+}
+
+/**
+ * Make a struct
+ */
+void t_php_generator::generate_struct(t_struct* tstruct) {
+ generate_php_struct(tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_php_generator::generate_xception(t_struct* txception) {
+ generate_php_struct(txception, true);
+}
+
+/**
+ * Structs can be normal or exceptions.
+ */
+void t_php_generator::generate_php_struct(t_struct* tstruct, bool is_exception) {
+ ofstream_with_content_based_conditional_update& f_struct = f_types_;
+ if (!classmap_) {
+ string f_struct_name = package_dir_ + tstruct->get_name() + ".php";
+ f_struct.open(f_struct_name.c_str());
+ generate_program_header(f_struct);
+ }
+ generate_php_struct_definition(f_struct, tstruct, is_exception);
+ if (!classmap_) {
+ f_struct.close();
+ }
+}
+
+void t_php_generator::generate_php_type_spec(ostream& out, t_type* t) {
+ t = get_true_type(t);
+ indent(out) << "'type' => " << type_to_enum(t) << "," << endl;
+
+ if (t->is_base_type() || t->is_enum()) {
+ // Noop, type is all we need
+ } else if (t->is_struct() || t->is_xception()) {
+ indent(out) << "'class' => '" << php_namespace(t->get_program()) << t->get_name() << "',"
+ << endl;
+ } else if (t->is_map()) {
+ t_type* ktype = get_true_type(((t_map*)t)->get_key_type());
+ t_type* vtype = get_true_type(((t_map*)t)->get_val_type());
+ indent(out) << "'ktype' => " << type_to_enum(ktype) << "," << endl;
+ indent(out) << "'vtype' => " << type_to_enum(vtype) << "," << endl;
+ indent(out) << "'key' => array(" << endl;
+ indent_up();
+ generate_php_type_spec(out, ktype);
+ indent_down();
+ indent(out) << ")," << endl;
+ indent(out) << "'val' => array(" << endl;
+ indent_up();
+ generate_php_type_spec(out, vtype);
+ indent(out) << ")," << endl;
+ indent_down();
+ } else if (t->is_list() || t->is_set()) {
+ t_type* etype;
+ if (t->is_list()) {
+ etype = get_true_type(((t_list*)t)->get_elem_type());
+ } else {
+ etype = get_true_type(((t_set*)t)->get_elem_type());
+ }
+ indent(out) << "'etype' => " << type_to_enum(etype) << "," << endl;
+ indent(out) << "'elem' => array(" << endl;
+ indent_up();
+ generate_php_type_spec(out, etype);
+ indent(out) << ")," << endl;
+ indent_down();
+ } else {
+ throw "compiler error: no type for php struct spec field";
+ }
+}
+
+/**
+ * Generates the struct specification structure, which fully qualifies enough
+ * type information to generalize serialization routines.
+ */
+void t_php_generator::generate_php_struct_spec(ostream& out, t_struct* tstruct) {
+ indent(out) << "static public $_TSPEC = array(" << endl;
+ indent_up();
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ indent(out) << (*m_iter)->get_key() << " => array(" << endl;
+ indent_up();
+ out << indent() << "'var' => '" << (*m_iter)->get_name() << "'," << endl;
+ out << indent() << "'isRequired' => " << ((*m_iter)->get_req() == t_field::T_REQUIRED ? "true" : "false") << "," << endl;
+ generate_php_type_spec(out, t);
+ indent_down();
+ indent(out) << ")," << endl;
+ }
+
+ indent_down();
+ indent(out) << ");" << endl << endl;
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is nothing in PHP
+ * where the objects are all just associative arrays (unless of course we
+ * decide to start using objects for them...)
+ *
+ * @param tstruct The struct definition
+ */
+void t_php_generator::generate_php_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception,
+ bool is_result) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ generate_php_doc(out, tstruct);
+ out << "class " << php_namespace_declaration(tstruct);
+ if (is_exception) {
+ out << " extends "
+ << "TException";
+ } else if (oop_) {
+ out << " extends "
+ << "TBase";
+ }
+ if (json_serializable_) {
+ out << " implements JsonSerializable";
+ }
+ out << endl
+ << "{" << endl;
+ indent_up();
+
+ out << indent() << "static public $isValidate = " << (validate_ ? "true" : "false") << ";" << endl << endl;
+
+ generate_php_struct_spec(out, tstruct);
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ string dval = "null";
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) {
+ dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
+ }
+ generate_php_doc(out, *m_iter);
+ indent(out) << "public $" << (*m_iter)->get_name() << " = " << dval << ";" << endl;
+ }
+
+ out << endl;
+
+ // Generate constructor from array
+ string param = (members.size() > 0) ? "$vals = null" : "";
+ out << indent() << "public function __construct(" << param << ")"<< endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_type* t = get_true_type((*m_iter)->get_type());
+ if ((*m_iter)->get_value() != NULL && (t->is_struct() || t->is_xception())) {
+ indent(out) << "$this->" << (*m_iter)->get_name() << " = "
+ << render_const_value(t, (*m_iter)->get_value()) << ";" << endl;
+ }
+ }
+ out << indent() << "if (is_array($vals)) {" << endl;
+ indent_up();
+ if (oop_) {
+ out << indent() << "parent::__construct(self::$_TSPEC, $vals);" << endl;
+ } else {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << indent() << "if (isset($vals['" << (*m_iter)->get_name() << "'])) {" << endl;
+
+ indent_up();
+ out << indent() << "$this->" << (*m_iter)->get_name() << " = $vals['"
+ << (*m_iter)->get_name() << "'];" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ }
+ indent_down();
+ out << indent() << "}" << endl;
+ }
+ scope_down(out);
+ out << endl;
+
+ out << indent() << "public function getName()" << endl
+ << indent() << "{" << endl;
+
+ indent_up();
+ out << indent() << "return '" << tstruct->get_name() << "';" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl << endl;
+
+ out << endl;
+ generate_php_struct_reader(out, tstruct, is_result);
+ out << endl;
+ generate_php_struct_writer(out, tstruct, is_result);
+ if (needs_php_read_validator(tstruct, is_result)) {
+ out << endl;
+ generate_php_struct_read_validator(out, tstruct);
+ }
+ if (needs_php_write_validator(tstruct, is_result)) {
+ out << endl;
+ generate_php_struct_write_validator(out, tstruct);
+ }
+ if (json_serializable_) {
+ out << endl;
+ generate_php_struct_json_serialize(out, tstruct, is_result);
+ }
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+/**
+ * Generates the read() method for a struct
+ */
+void t_php_generator::generate_php_struct_reader(ostream& out, t_struct* tstruct, bool is_result) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "public function read($input)" << endl;
+ scope_up(out);
+
+ if (oop_) {
+ if (needs_php_read_validator(tstruct, is_result)) {
+ indent(out) << "$tmp = $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);"
+ << endl;
+ indent(out) << "$this->_validateForRead();" << endl;
+ indent(out) << "return $tmp;" << endl;
+ } else {
+ indent(out) << "return $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);"
+ << endl;
+ }
+ scope_down(out);
+ out << endl;
+ return;
+ }
+
+ out << indent() << "$xfer = 0;" << endl << indent() << "$fname = null;" << endl << indent()
+ << "$ftype = 0;" << endl << indent() << "$fid = 0;" << endl;
+
+ // Declare stack tmp variables
+ if (!binary_inline_) {
+ indent(out) << "$xfer += $input->readStructBegin($fname);" << endl;
+ }
+
+ // Loop over reading in fields
+ indent(out) << "while (true) {" << endl;
+
+ indent_up();
+
+ // Read beginning field marker
+ if (binary_inline_) {
+ t_field fftype(g_type_i8, "ftype");
+ t_field ffid(g_type_i16, "fid");
+ generate_deserialize_field(out, &fftype);
+ out << indent() << "if ($ftype == "
+ << "TType::STOP) {" << endl << indent() << " break;" << endl << indent() << "}" << endl;
+ generate_deserialize_field(out, &ffid);
+ } else {
+ indent(out) << "$xfer += $input->readFieldBegin($fname, $ftype, $fid);" << endl;
+ // Check for field STOP marker and break
+ indent(out) << "if ($ftype == "
+ << "TType::STOP) {" << endl;
+ indent_up();
+ indent(out) << "break;" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ // Switch statement on the field we are reading
+ indent(out) << "switch ($fid) {" << endl;
+
+ indent_up();
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ indent(out) << "if ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
+ indent_up();
+ generate_deserialize_field(out, *f_iter, "this->");
+ indent_down();
+ out << indent() << "} else {" << endl;
+
+ indent_up();
+ if (binary_inline_) {
+ indent(out) << "$xfer += TProtocol::skipBinary($input, $ftype);" << endl;
+ } else {
+ indent(out) << "$xfer += $input->skip($ftype);" << endl;
+ }
+
+ indent_down();
+ out << indent() << "}" << endl << indent() << "break;" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ indent(out) << "default:" << endl;
+
+ indent_up();
+ if (binary_inline_) {
+ indent(out) << "$xfer += "
+ << "TProtocol::skipBinary($input, $ftype);" << endl;
+ } else {
+ indent(out) << "$xfer += $input->skip($ftype);" << endl;
+ }
+ indent(out) << "break;" << endl;
+ indent_down();
+
+ scope_down(out);
+
+ if (!binary_inline_) {
+ // Read field end marker
+ indent(out) << "$xfer += $input->readFieldEnd();" << endl;
+ }
+
+ scope_down(out);
+
+ if (!binary_inline_) {
+ indent(out) << "$xfer += $input->readStructEnd();" << endl;
+ }
+
+ if (needs_php_read_validator(tstruct, is_result)) {
+ indent(out) << "$this->_validateForRead();" << endl;
+ }
+
+ indent(out) << "return $xfer;" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+/**
+ * Generates the write() method for a struct
+ */
+void t_php_generator::generate_php_struct_writer(ostream& out, t_struct* tstruct, bool is_result) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (binary_inline_) {
+ indent(out) << "public function write(&$output)" << endl;
+ } else {
+ indent(out) << "public function write($output)" << endl;
+ }
+ indent(out) << "{" << endl;
+ indent_up();
+
+ if (needs_php_write_validator(tstruct, is_result)) {
+ indent(out) << "$this->_validateForWrite();" << endl;
+ }
+
+ if (oop_) {
+ indent(out) << "return $this->_write('" << tstruct->get_name() << "', self::$_TSPEC, $output);"
+ << endl;
+ scope_down(out);
+ out << endl;
+ return;
+ }
+
+ indent(out) << "$xfer = 0;" << endl;
+
+ if (!binary_inline_) {
+ indent(out) << "$xfer += $output->writeStructBegin('" << name << "');" << endl;
+ }
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ out << indent() << "if ($this->" << (*f_iter)->get_name() << " !== null) {" << endl;
+ indent_up();
+
+ t_type* type = get_true_type((*f_iter)->get_type());
+ string expect;
+ if (type->is_container()) {
+ expect = "array";
+ } else if (type->is_struct()) {
+ expect = "object";
+ }
+ if (!expect.empty()) {
+ out << indent() << "if (!is_" << expect << "($this->" << (*f_iter)->get_name() << ")) {"
+ << endl;
+ indent_up();
+ out << indent() << "throw new "
+ << "TProtocolException('Bad type in structure.', "
+ << "TProtocolException::INVALID_DATA);" << endl;
+ scope_down(out);
+ }
+
+ // Write field header
+ if (binary_inline_) {
+ out << indent() << "$output .= pack('c', " << type_to_enum((*f_iter)->get_type()) << ");"
+ << endl << indent() << "$output .= pack('n', " << (*f_iter)->get_key() << ");" << endl;
+ } else {
+ indent(out) << "$xfer += $output->writeFieldBegin("
+ << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type())
+ << ", " << (*f_iter)->get_key() << ");" << endl;
+ }
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "this->");
+
+ // Write field closer
+ if (!binary_inline_) {
+ indent(out) << "$xfer += $output->writeFieldEnd();" << endl;
+ }
+
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+
+ if (binary_inline_) {
+ out << indent() << "$output .= pack('c', "
+ << "TType::STOP);" << endl;
+ } else {
+ out << indent() << "$xfer += $output->writeFieldStop();" << endl << indent()
+ << "$xfer += $output->writeStructEnd();" << endl;
+ }
+
+ out << indent() << "return $xfer;" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+void t_php_generator::generate_php_struct_read_validator(ostream& out, t_struct* tstruct) {
+ generate_php_struct_required_validator(out, tstruct, "_validateForRead", false);
+}
+
+void t_php_generator::generate_php_struct_write_validator(ostream& out, t_struct* tstruct) {
+ generate_php_struct_required_validator(out, tstruct, "_validateForWrite", true);
+}
+
+void t_php_generator::generate_php_struct_required_validator(ostream& out,
+ t_struct* tstruct,
+ std::string method_name,
+ bool write_mode) {
+ indent(out) << "private function " << method_name << "() {" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+
+ if (fields.size() > 0) {
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ if (field->get_req() == t_field::T_REQUIRED
+ || (field->get_req() == t_field::T_OPT_IN_REQ_OUT && write_mode)) {
+ indent(out) << "if ($this->" << field->get_name() << " === null) {" << endl;
+ indent_up();
+ indent(out) << "throw new TProtocolException('Required field " << tstruct->get_name() << "."
+ << field->get_name() << " is unset!');" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+ }
+
+ indent_down();
+ indent(out) << "}" << endl;
+}
+
+void t_php_generator::generate_php_struct_json_serialize(ostream& out,
+ t_struct* tstruct,
+ bool is_result) {
+ indent(out) << "public function jsonSerialize() {" << endl;
+ indent_up();
+
+ if (needs_php_write_validator(tstruct, is_result)) {
+ indent(out) << "$this->_validateForWrite();" << endl;
+ }
+
+ indent(out) << "$json = new stdClass;" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+
+ if (fields.size() > 0) {
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ t_type* type = field->get_type();
+ const string& name = field->get_name();
+ if (type->is_map()) {
+ t_type* key_type = ((t_map*)type)->get_key_type();
+ if (!(key_type->is_base_type() || key_type->is_enum())) {
+ // JSON object keys must be strings. PHP's json_encode()
+ // function will convert any scalar key to strings, but
+ // we skip thrift maps with non-scalar keys.
+ continue;
+ }
+ }
+ indent(out) << "if ($this->" << name << " !== null) {" << endl;
+ indent_up();
+ indent(out) << "$json->" << name << " = ";
+ if (type->is_map()) {
+ out << "(object)";
+ } else {
+ out << type_to_cast(type);
+ }
+ out << "$this->" << name << ";" << endl;
+ indent_down();
+ indent(out) << "}" << endl;
+ }
+ }
+
+ indent(out) << "return $json;" << endl;
+ indent_down();
+
+ indent(out) << "}" << endl;
+}
+
+int t_php_generator::get_php_num_required_fields(const vector<t_field*>& fields, bool write_mode) {
+ int num_req = 0;
+
+ if (fields.size() > 0) {
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_req() == t_field::T_REQUIRED
+ || ((*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT && write_mode)) {
+ ++num_req;
+ }
+ }
+ }
+ return num_req;
+}
+
+bool t_php_generator::needs_php_write_validator(t_struct* tstruct, bool is_result) {
+ return (validate_ && !is_result && !tstruct->is_union()
+ && get_php_num_required_fields(tstruct->get_members(), true) > 0);
+}
+
+bool t_php_generator::needs_php_read_validator(t_struct* tstruct, bool is_result) {
+ return (validate_ && !is_result
+ && (get_php_num_required_fields(tstruct->get_members(), false) > 0));
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_php_generator::generate_service(t_service* tservice) {
+ if(classmap_) {
+ string f_service_name = package_dir_ + service_name_ + ".php";
+ f_service_.open(f_service_name.c_str());
+ generate_service_header(tservice, f_service_);
+ }
+
+ // Generate the three main parts of the service (well, two for now in PHP)
+ generate_service_interface(tservice);
+ if (rest_) {
+ generate_service_rest(tservice);
+ }
+ generate_service_client(tservice);
+ generate_service_helpers(tservice);
+ if (phps_) {
+ generate_service_processor(tservice);
+ }
+
+ if(classmap_) {
+ // Close service file
+ f_service_ << endl;
+ f_service_.close();
+ }
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_php_generator::generate_service_processor(t_service* tservice) {
+ ofstream_with_content_based_conditional_update& f_service_processor = f_service_;
+ if (!classmap_) {
+ string f_service_processor_name = package_dir_ + service_name_ + "Processor.php";
+ f_service_processor.open(f_service_processor_name.c_str());
+ generate_service_header(tservice, f_service_processor);
+ }
+
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = tservice->get_extends()->get_name();
+ extends_processor = " extends " + php_namespace(tservice->get_extends()->get_program())
+ + extends + "Processor";
+ }
+
+ // Generate the header portion
+ f_service_processor << "class " << service_name_ << "Processor" << extends_processor << endl
+ << "{" << endl;
+ indent_up();
+
+ if (extends.empty()) {
+ f_service_processor << indent() << "protected $handler_ = null;" << endl;
+ }
+
+ f_service_processor << indent() << "public function __construct($handler)"<< endl
+ << indent() << "{" << endl;
+
+ indent_up();
+ if (extends.empty()) {
+ f_service_processor << indent() << "$this->handler_ = $handler;" << endl;
+ } else {
+ f_service_processor << indent() << "parent::__construct($handler);" << endl;
+ }
+
+ indent_down();
+ f_service_processor << indent() << "}" << endl << endl;
+
+ // Generate the server implementation
+ f_service_processor << indent() << "public function process($input, $output)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ f_service_processor << indent() << "$rseqid = 0;" << endl << indent() << "$fname = null;" << endl
+ << indent() << "$mtype = 0;" << endl << endl;
+
+ if (binary_inline_) {
+ t_field ffname(g_type_string, "fname");
+ t_field fmtype(g_type_i8, "mtype");
+ t_field fseqid(g_type_i32, "rseqid");
+ generate_deserialize_field(f_service_processor, &ffname, "", true);
+ generate_deserialize_field(f_service_processor, &fmtype, "", true);
+ generate_deserialize_field(f_service_processor, &fseqid, "", true);
+ } else {
+ f_service_processor << indent() << "$input->readMessageBegin($fname, $mtype, $rseqid);" << endl;
+ }
+
+ // HOT: check for method implementation
+ f_service_processor << indent() << "$methodname = 'process_'.$fname;" << endl
+ << indent() << "if (!method_exists($this, $methodname)) {" << endl;
+
+ indent_up();
+ if (binary_inline_) {
+ f_service_processor << indent() << "throw new \\Exception('Function '.$fname.' not implemented.');" << endl;
+ } else {
+ f_service_processor << indent() << " $input->skip("
+ << "TType::STRUCT);" << endl << indent() << " $input->readMessageEnd();" << endl
+ << indent() << " $x = new "
+ << "TApplicationException('Function '.$fname.' not implemented.', "
+ << "TApplicationException::UNKNOWN_METHOD);" << endl << indent()
+ << " $output->writeMessageBegin($fname, "
+ << "TMessageType::EXCEPTION, $rseqid);" << endl << indent()
+ << " $x->write($output);" << endl << indent() << " $output->writeMessageEnd();"
+ << endl << indent() << " $output->getTransport()->flush();" << endl << indent()
+ << " return;" << endl;
+ }
+
+ indent_down();
+ f_service_processor << indent() << "}" << endl
+ << indent() << "$this->$methodname($rseqid, $input, $output);" << endl
+ << indent() << "return true;" << endl;
+
+ indent_down();
+ f_service_processor << indent() << "}" << endl << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(f_service_processor, tservice, *f_iter);
+ }
+
+ indent_down();
+ f_service_processor << "}" << endl;
+
+ if (!classmap_) {
+ f_service_processor.close();
+ }
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_php_generator::generate_process_function(std::ostream& out, t_service* tservice, t_function* tfunction) {
+ // Open function
+ out << indent() << "protected function process_" << tfunction->get_name() << "($seqid, $input, $output)" << endl
+ << indent() << "{" << endl;
+ indent_up();
+
+ string argsname = php_namespace(tservice->get_program()) + service_name_ + "_"
+ + tfunction->get_name() + "_args";
+ string resultname = php_namespace(tservice->get_program()) + service_name_ + "_"
+ + tfunction->get_name() + "_result";
+
+ out << indent() << "$bin_accel = ($input instanceof "
+ << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_read_binary_after_message_begin');"
+ << endl;
+ out << indent() << "if ($bin_accel) {" << endl;
+ indent_up();
+
+ out << indent() << "$args = thrift_protocol_read_binary_after_message_begin(" <<endl;
+
+ indent_up();
+ out << indent() << "$input,"<<endl
+ << indent() << "'" << argsname << "'," << endl
+ << indent() << "$input->isStrictRead()" <<endl;
+
+ indent_down();
+ out << indent() <<");" << endl;
+
+ indent_down();
+ out << indent() << "} else {" << endl;
+
+ indent_up();
+ out << indent() << "$args = new " << argsname << "();" << endl
+ << indent() << "$args->read($input);" << endl;
+
+ indent_down();
+ out << indent() << "}" << endl;
+
+ if (!binary_inline_) {
+ out << indent() << "$input->readMessageEnd();" << endl;
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ out << indent() << "$result = new " << resultname << "();" << endl;
+ }
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ out << indent() << "try {" << endl;
+ indent_up();
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ out << "$result->success = ";
+ }
+ out << "$this->handler_->" << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ out << ", ";
+ }
+ out << "$args->" << (*f_iter)->get_name();
+ }
+ out << ");" << endl;
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ indent_down();
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << indent() << "} catch ("
+ << php_namespace(get_true_type((*x_iter)->get_type())->get_program())
+ << (*x_iter)->get_type()->get_name() << " $" << (*x_iter)->get_name() << ") {"
+ << endl;
+ if (!tfunction->is_oneway()) {
+ indent_up();
+ out << indent() << "$result->" << (*x_iter)->get_name() << " = $"
+ << (*x_iter)->get_name() << ";" << endl;
+ indent_down();
+ out << indent();
+ }
+ }
+ out << "}" << endl;
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ out << indent() << "return;" << endl;
+ indent_down();
+ out << indent() << "}" << endl;
+ return;
+ }
+
+ out << indent() << "$bin_accel = ($output instanceof "
+ << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');"
+ << endl;
+
+ out << indent() << "if ($bin_accel) {" << endl;
+ indent_up();
+
+ out << indent() << "thrift_protocol_write_binary(" << endl;
+
+ indent_up();
+ out << indent() << "$output,"<<endl
+ << indent() << "'" << tfunction->get_name()<< "'," <<endl
+ << indent() << "TMessageType::REPLY,"<< endl
+ << indent() << "$result," << endl
+ << indent() << "$seqid," << endl
+ << indent() << "$output->isStrictWrite()"<<endl;
+
+ indent_down();
+ out << indent() << ");" << endl;
+
+ indent_down();
+ out << indent() << "} else {" << endl;
+ indent_up();
+
+ // Serialize the request header
+ if (binary_inline_) {
+ out << indent() << "$buff = pack('N', (0x80010000 | "
+ << "TMessageType::REPLY)); " << endl << indent() << "$buff .= pack('N', strlen('"
+ << tfunction->get_name() << "'));" << endl << indent() << "$buff .= '"
+ << tfunction->get_name() << "';" << endl << indent() << "$buff .= pack('N', $seqid);"
+ << endl << indent() << "$result->write($buff);" << endl << indent()
+ << "$output->write($buff);" << endl << indent() << "$output->flush();" << endl;
+ } else {
+ out << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', "
+ << "TMessageType::REPLY, $seqid);" << endl << indent() << "$result->write($output);"
+ << endl << indent() << "$output->writeMessageEnd();" << endl << indent()
+ << "$output->getTransport()->flush();" << endl;
+ }
+
+ scope_down(out);
+
+ // Close function
+ indent_down();
+ out << indent() << "}" << endl;
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_php_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ ofstream_with_content_based_conditional_update& f_struct_definition = f_service_;
+ if (classmap_) {
+ f_struct_definition << "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
+ }
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ string name = ts->get_name();
+ ts->set_name(service_name_ + "_" + name);
+
+ if (!classmap_) {
+ string f_struct_definition_name = package_dir_ + service_name_ + "_" + name + ".php";
+ f_struct_definition.open(f_struct_definition_name.c_str());
+ generate_service_header(tservice, f_struct_definition);
+ }
+
+ generate_php_struct_definition(f_struct_definition, ts);
+ if (!classmap_) {
+ f_struct_definition.close();
+ }
+
+ generate_php_function_helpers(tservice, *f_iter);
+ ts->set_name(name);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_php_generator::generate_php_function_helpers(t_service* tservice, t_function* tfunction) {
+ if (!tfunction->is_oneway()) {
+ t_struct result(program_, service_name_ + "_" + tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+
+ ofstream_with_content_based_conditional_update& f_struct_helper = f_service_;
+ if (!classmap_) {
+ string f_struct_helper_name = package_dir_ + result.get_name() + ".php";
+ f_struct_helper.open(f_struct_helper_name.c_str());
+ generate_service_header(tservice, f_struct_helper);
+ }
+ generate_php_struct_definition(f_struct_helper, &result, false, true);
+ if (!classmap_) {
+ f_struct_helper.close();
+ }
+ }
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_php_generator::generate_service_interface(t_service* tservice) {
+ ofstream_with_content_based_conditional_update& f_service_interface = f_service_;
+ if (!classmap_) {
+ string f_service_interface_name = package_dir_ + service_name_ + "If.php";
+ f_service_interface.open(f_service_interface_name.c_str());
+ generate_service_header(tservice, f_service_interface);
+ }
+
+ string extends = "";
+ string extends_if = "";
+ if (tservice->get_extends() != NULL) {
+ extends = " extends " + php_namespace(tservice->get_extends()->get_program())
+ + tservice->get_extends()->get_name();
+ extends_if = " extends " + php_namespace(tservice->get_extends()->get_program())
+ + tservice->get_extends()->get_name() + "If";
+ }
+ generate_php_doc(f_service_interface, tservice);
+ f_service_interface << "interface " << php_namespace_declaration(tservice) << "If" << extends_if << endl
+ << "{" << endl;
+
+ indent_up();
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_php_doc(f_service_interface, *f_iter);
+ indent(f_service_interface) << "public function " << function_signature(*f_iter) << ";" << endl;
+ }
+ indent_down();
+ f_service_interface << "}" << endl;
+
+ // Close service interface file
+ if (!classmap_) {
+ f_service_interface.close();
+ }
+}
+
+/**
+ * Generates a REST interface
+ */
+void t_php_generator::generate_service_rest(t_service* tservice) {
+ ofstream_with_content_based_conditional_update& f_service_rest = f_service_;
+ if (!classmap_) {
+ string f_service_rest_name = package_dir_ + service_name_ + "Rest.php";
+ f_service_rest.open(f_service_rest_name.c_str());
+ generate_service_header(tservice, f_service_rest);
+ }
+
+ string extends = "";
+ string extends_if = "";
+ if (tservice->get_extends() != NULL) {
+ extends = " extends " + php_namespace(tservice->get_extends()->get_program())
+ + tservice->get_extends()->get_name();
+ extends_if = " extends " + php_namespace(tservice->get_extends()->get_program())
+ + tservice->get_extends()->get_name() + "Rest";
+ }
+ f_service_rest << "class " << service_name_ << "Rest" << extends_if << endl
+ << "{" << endl;
+ indent_up();
+
+ if (extends.empty()) {
+ f_service_rest << indent() << "protected $impl_;" << endl << endl;
+ }
+
+ f_service_rest << indent() << "public function __construct($impl) {" << endl << indent()
+ << " $this->impl_ = $impl;" << endl << indent() << "}" << endl << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ indent(f_service_rest) << "public function " << (*f_iter)->get_name() << "($request) {" << endl;
+ indent_up();
+ const vector<t_field*>& args = (*f_iter)->get_arglist()->get_members();
+ vector<t_field*>::const_iterator a_iter;
+ for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
+ t_type* atype = get_true_type((*a_iter)->get_type());
+ string cast = type_to_cast(atype);
+ string req = "$request['" + (*a_iter)->get_name() + "']";
+ if (atype->is_bool()) {
+ f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = " << cast << "(!empty(" << req
+ << ") && (" << req << " !== 'false'));" << endl;
+ } else {
+ f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = isset(" << req << ") ? "
+ << cast << req << " : null;" << endl;
+ }
+ if (atype->is_string() && ((t_base_type*)atype)->is_string_list()) {
+ f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = explode(',', $"
+ << (*a_iter)->get_name() << ");" << endl;
+ } else if (atype->is_map() || atype->is_list()) {
+ f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = json_decode($"
+ << (*a_iter)->get_name() << ", true);" << endl;
+ } else if (atype->is_set()) {
+ f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = array_fill_keys(json_decode($"
+ << (*a_iter)->get_name() << ", true), 1);" << endl;
+ } else if (atype->is_struct() || atype->is_xception()) {
+ f_service_rest << indent() << "if ($" << (*a_iter)->get_name() << " !== null) {" << endl
+ << indent() << " $" << (*a_iter)->get_name() << " = new "
+ << php_namespace(atype->get_program()) << atype->get_name() << "(json_decode($"
+ << (*a_iter)->get_name() << ", true));" << endl << indent() << "}" << endl;
+ }
+ }
+ f_service_rest << indent() << "return $this->impl_->" << (*f_iter)->get_name() << "("
+ << argument_list((*f_iter)->get_arglist(), false) << ");" << endl;
+ indent_down();
+ indent(f_service_rest) << "}" << endl << endl;
+ }
+ indent_down();
+ f_service_rest << "}" << endl << endl;
+
+ // Close service rest file
+ f_service_rest << endl;
+ if (!classmap_) {
+ f_service_rest.close();
+ }
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_php_generator::generate_service_client(t_service* tservice) {
+ ofstream_with_content_based_conditional_update& f_service_client = f_service_;
+ if (!classmap_) {
+ string f_service_client_name = package_dir_ + service_name_ + "Client.php";
+ f_service_client.open(f_service_client_name.c_str());
+ generate_service_header(tservice, f_service_client);
+ }
+
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = tservice->get_extends()->get_name();
+ extends_client = " extends " + php_namespace(tservice->get_extends()->get_program()) + extends
+ + "Client";
+ }
+
+ f_service_client << "class " << php_namespace_declaration(tservice) << "Client" << extends_client
+ << " implements " << php_namespace(tservice->get_program()) << service_name_ << "If" << endl
+ <<"{"<< endl;
+ indent_up();
+
+ // Private members
+ if (extends.empty()) {
+ f_service_client << indent() << "protected $input_ = null;" << endl << indent()
+ << "protected $output_ = null;" << endl << endl;
+ f_service_client << indent() << "protected $seqid_ = 0;" << endl << endl;
+ }
+
+ // Constructor function
+ f_service_client << indent() << "public function __construct($input, $output = null)" << endl
+ << indent() << "{" << endl;
+
+ indent_up();
+ if (!extends.empty()) {
+ f_service_client << indent() << "parent::__construct($input, $output);" << endl;
+ } else {
+ f_service_client << indent() << "$this->input_ = $input;" << endl
+ << indent() << "$this->output_ = $output ? $output : $input;" << endl;
+ }
+
+ indent_down();
+ f_service_client << indent() << "}" << endl << endl;
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+
+ f_service_client << endl;
+
+ // Open function
+ indent(f_service_client) << "public function " << function_signature(*f_iter) << endl;
+ scope_up(f_service_client);
+ indent(f_service_client) << "$this->send_" << funname << "(";
+
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_client << ", ";
+ }
+ f_service_client << "$" << (*fld_iter)->get_name();
+ }
+ f_service_client << ");" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_client << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_client << "return ";
+ }
+ f_service_client << "$this->recv_" << funname << "();" << endl;
+ }
+ scope_down(f_service_client);
+ f_service_client << endl;
+
+ indent(f_service_client) << "public function send_" << function_signature(*f_iter) << endl;
+ scope_up(f_service_client);
+
+ std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_"
+ + (*f_iter)->get_name() + "_args";
+
+ f_service_client << indent() << "$args = new " << argsname << "();" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_client << indent() << "$args->" << (*fld_iter)->get_name() << " = $"
+ << (*fld_iter)->get_name() << ";" << endl;
+ }
+
+ f_service_client << indent() << "$bin_accel = ($this->output_ instanceof "
+ << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');"
+ << endl;
+
+ f_service_client << indent() << "if ($bin_accel) {" << endl;
+ indent_up();
+
+ string messageType = (*f_iter)->is_oneway() ? "TMessageType::ONEWAY" : "TMessageType::CALL";
+
+ f_service_client << indent() << "thrift_protocol_write_binary(" << endl;
+
+ indent_up();
+ f_service_client << indent() << "$this->output_," << endl
+ << indent() << "'" << (*f_iter)->get_name() << "'," << endl
+ << indent() << messageType << "," << endl
+ << indent() << "$args," << endl
+ << indent() << "$this->seqid_," << endl
+ << indent() << "$this->output_->isStrictWrite()" << endl;
+
+ indent_down();
+ f_service_client << indent() << ");" << endl;
+
+ indent_down();
+ f_service_client << indent() << "} else {" << endl;
+ indent_up();
+
+ // Serialize the request header
+ if (binary_inline_) {
+ f_service_client << indent() << "$buff = pack('N', (0x80010000 | " << messageType << "));" << endl
+ << indent() << "$buff .= pack('N', strlen('" << funname << "'));" << endl
+ << indent() << "$buff .= '" << funname << "';" << endl << indent()
+ << "$buff .= pack('N', $this->seqid_);" << endl;
+ } else {
+ f_service_client << indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name()
+ << "', " << messageType << ", $this->seqid_);" << endl;
+ }
+
+ // Write to the stream
+ if (binary_inline_) {
+ f_service_client << indent() << "$args->write($buff);" << endl << indent()
+ << "$this->output_->write($buff);" << endl << indent()
+ << "$this->output_->flush();" << endl;
+ } else {
+ f_service_client << indent() << "$args->write($this->output_);" << endl << indent()
+ << "$this->output_->writeMessageEnd();" << endl << indent()
+ << "$this->output_->getTransport()->flush();" << endl;
+ }
+
+ scope_down(f_service_client);
+
+ scope_down(f_service_client);
+
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultname = php_namespace(tservice->get_program()) + service_name_ + "_"
+ + (*f_iter)->get_name() + "_result";
+ t_struct noargs(program_);
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ // Open function
+ f_service_client << endl << indent() << "public function " << function_signature(&recv_function)
+ << endl;
+ scope_up(f_service_client);
+
+ f_service_client << indent() << "$bin_accel = ($this->input_ instanceof "
+ << "TBinaryProtocolAccelerated)"
+ << " && function_exists('thrift_protocol_read_binary');" << endl;
+
+ f_service_client << indent() << "if ($bin_accel) {" << endl;
+
+ indent_up();
+ f_service_client << indent() << "$result = thrift_protocol_read_binary(" << endl;
+
+ indent_up();
+ f_service_client << indent() << "$this->input_," << endl
+ << indent() << "'" << resultname << "'," << endl
+ << indent() << "$this->input_->isStrictRead()" << endl;
+
+ indent_down();
+ f_service_client << indent() << ");" << endl;
+
+ indent_down();
+ f_service_client << indent() << "} else {" << endl;
+
+ indent_up();
+ f_service_client << indent() << "$rseqid = 0;" << endl
+ << indent() << "$fname = null;" << endl
+ << indent() << "$mtype = 0;" << endl << endl;
+
+ if (binary_inline_) {
+ t_field ffname(g_type_string, "fname");
+ t_field fseqid(g_type_i32, "rseqid");
+ f_service_client << indent() << "$ver = unpack('N', $this->input_->readAll(4));" << endl
+ << indent() << "$ver = $ver[1];" << endl << indent() << "$mtype = $ver & 0xff;"
+ << endl << indent() << "$ver = $ver & 0xffff0000;" << endl << indent()
+ << "if ($ver != 0x80010000) throw new "
+ << "TProtocolException('Bad version identifier: '.$ver, "
+ << "TProtocolException::BAD_VERSION);" << endl;
+ generate_deserialize_field(f_service_client, &ffname, "", true);
+ generate_deserialize_field(f_service_client, &fseqid, "", true);
+ } else {
+ f_service_client << indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" << endl
+ << indent() << "if ($mtype == TMessageType::EXCEPTION) {" << endl;
+
+ indent_up();
+ f_service_client << indent() << "$x = new TApplicationException();" << endl
+ << indent() << "$x->read($this->input_);" << endl
+ << indent() << "$this->input_->readMessageEnd();" << endl
+ << indent() << "throw $x;" << endl;
+ indent_down();
+ f_service_client << indent() << "}" << endl;
+ }
+
+ f_service_client << indent() << "$result = new " << resultname << "();" << endl
+ << indent() << "$result->read($this->input_);" << endl;
+
+ if (!binary_inline_) {
+ f_service_client << indent() << "$this->input_->readMessageEnd();" << endl;
+ }
+
+ scope_down(f_service_client);
+
+ // Careful, only return result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_client << indent() << "if ($result->success !== null) {" << endl;
+
+ indent_up();
+ f_service_client << indent() << "return $result->success;" << endl;
+
+ indent_down();
+ f_service_client << indent() << "}" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_client << indent() << "if ($result->" << (*x_iter)->get_name() << " !== null) {" << endl;
+
+ indent_up();
+ f_service_client << indent() << "throw $result->" << (*x_iter)->get_name() << ";" << endl;
+
+ indent_down();
+ f_service_client << indent() << "}" << endl;
+ }
+
+ // Careful, only return _result if not a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(f_service_client) << "return;" << endl;
+ } else {
+ f_service_client << indent() << "throw new \\Exception(\"" << (*f_iter)->get_name()
+ << " failed: unknown result\");" << endl;
+ }
+
+ // Close function
+ scope_down(f_service_client);
+ }
+ }
+
+ indent_down();
+ f_service_client << "}" << endl;
+
+ // Close service client file
+ if (!classmap_) {
+ f_service_client.close();
+ }
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_php_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix,
+ bool inclass) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else {
+
+ if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ if (binary_inline_) {
+ std::string itrans = (inclass ? "$this->input_" : "$input");
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << indent() << "$len = unpack('N', " << itrans << "->readAll(4));" << endl
+ << indent() << "$len = $len[1];" << endl << indent() << "if ($len > 0x7fffffff) {"
+ << endl << indent() << " $len = 0 - (($len - 1) ^ 0xffffffff);" << endl << indent()
+ << "}" << endl << indent() << "$" << name << " = " << itrans << "->readAll($len);"
+ << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));"
+ << endl << indent() << "$" << name << " = (bool)$" << name << "[1];" << endl;
+ break;
+ case t_base_type::TYPE_I8:
+ out << indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));"
+ << endl << indent() << "$" << name << " = $" << name << "[1];" << endl;
+ break;
+ case t_base_type::TYPE_I16:
+ out << indent() << "$val = unpack('n', " << itrans << "->readAll(2));" << endl
+ << indent() << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fff) {"
+ << endl << indent() << " $val = 0 - (($val - 1) ^ 0xffff);" << endl << indent()
+ << "}" << endl << indent() << "$" << name << " = $val;" << endl;
+ break;
+ case t_base_type::TYPE_I32:
+ out << indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl
+ << indent() << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fffffff) {"
+ << endl << indent() << " $val = 0 - (($val - 1) ^ 0xffffffff);" << endl << indent()
+ << "}" << endl << indent() << "$" << name << " = $val;" << endl;
+ break;
+ case t_base_type::TYPE_I64:
+ out << indent() << "$arr = unpack('N2', " << itrans << "->readAll(8));" << endl
+ << indent() << "if ($arr[1] & 0x80000000) {" << endl << indent()
+ << " $arr[1] = $arr[1] ^ 0xFFFFFFFF;" << endl << indent()
+ << " $arr[2] = $arr[2] ^ 0xFFFFFFFF;" << endl << indent() << " $" << name
+ << " = 0 - $arr[1]*4294967296 - $arr[2] - 1;" << endl << indent() << "} else {"
+ << endl << indent() << " $" << name << " = $arr[1]*4294967296 + $arr[2];" << endl
+ << indent() << "}" << endl;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << indent() << "$arr = unpack('d', strrev(" << itrans << "->readAll(8)));" << endl
+ << indent() << "$" << name << " = $arr[1];" << endl;
+ break;
+ default:
+ throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase)
+ + tfield->get_name();
+ }
+ } else if (type->is_enum()) {
+ out << indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl << indent()
+ << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fffffff) {" << endl
+ << indent() << " $val = 0 - (($val - 1) ^ 0xffffffff);" << endl << indent() << "}"
+ << endl << indent() << "$" << name << " = $val;" << endl;
+ }
+ } else {
+
+ indent(out) << "$xfer += $input->";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "readString($" << name << ");";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool($" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte($" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16($" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32($" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64($" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble($" << name << ");";
+ break;
+ default:
+ throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32($" << name << ");";
+ }
+ out << endl;
+ }
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+ }
+}
+
+/**
+ * Generates an unserializer for a variable. This makes two key assumptions,
+ * first that there is a const char* variable named data that points to the
+ * buffer for deserialization, and that there is a variable protocol which
+ * is a reference to a TProtocol serialization object.
+ */
+void t_php_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ out << indent() << "$" << prefix << " = new " << php_namespace(tstruct->get_program())
+ << tstruct->get_name() << "();" << endl << indent() << "$xfer += $" << prefix
+ << "->read($input);" << endl;
+}
+
+void t_php_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+
+ t_field fsize(g_type_i32, size);
+ t_field fktype(g_type_i8, ktype);
+ t_field fvtype(g_type_i8, vtype);
+ t_field fetype(g_type_i8, etype);
+
+ out << indent() << "$" << prefix << " = array();" << endl << indent() << "$" << size << " = 0;"
+ << endl;
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ out << indent() << "$" << ktype << " = 0;" << endl << indent() << "$" << vtype << " = 0;"
+ << endl;
+ if (binary_inline_) {
+ generate_deserialize_field(out, &fktype);
+ generate_deserialize_field(out, &fvtype);
+ generate_deserialize_field(out, &fsize);
+ } else {
+ out << indent() << "$xfer += $input->readMapBegin("
+ << "$" << ktype << ", $" << vtype << ", $" << size << ");" << endl;
+ }
+ } else if (ttype->is_set()) {
+ if (binary_inline_) {
+ generate_deserialize_field(out, &fetype);
+ generate_deserialize_field(out, &fsize);
+ } else {
+ out << indent() << "$" << etype << " = 0;" << endl << indent()
+ << "$xfer += $input->readSetBegin("
+ << "$" << etype << ", $" << size << ");" << endl;
+ }
+ } else if (ttype->is_list()) {
+ if (binary_inline_) {
+ generate_deserialize_field(out, &fetype);
+ generate_deserialize_field(out, &fsize);
+ } else {
+ out << indent() << "$" << etype << " = 0;" << endl << indent()
+ << "$xfer += $input->readListBegin("
+ << "$" << etype << ", $" << size << ");" << endl;
+ }
+ }
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) << "for ($" << i << " = 0; $" << i << " < $" << size << "; ++$" << i << ") {" << endl;
+
+ indent_up();
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ scope_down(out);
+
+ if (!binary_inline_) {
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "$xfer += $input->readMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "$xfer += $input->readSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "$xfer += $input->readListEnd();" << endl;
+ }
+ }
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_php_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("key");
+ string val = tmp("val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ indent(out) << declare_field(&fkey, true, true) << endl;
+ indent(out) << declare_field(&fval, true, true) << endl;
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << "$" << prefix << "[$" << key << "] = $" << val << ";" << endl;
+}
+
+void t_php_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ indent(out) << "$" << elem << " = null;" << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ t_type* elem_type = tset->get_elem_type();
+ if(php_is_scalar(elem_type)) {
+ indent(out) << "$" << prefix << "[$" << elem << "] = true;" << endl;
+ } else {
+ indent(out) << "$" << prefix << "[] = $" << elem << ";" << endl;
+ }
+}
+
+void t_php_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ indent(out) << "$" << elem << " = null;" << endl;
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << "$" << prefix << " []= $" << elem << ";" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_php_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name());
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + tfield->get_name();
+
+ if (binary_inline_) {
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << indent() << "$output .= pack('N', strlen($" << name << "));" << endl << indent()
+ << "$output .= $" << name << ";" << endl;
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << indent() << "$output .= pack('c', $" << name << " ? 1 : 0);" << endl;
+ break;
+ case t_base_type::TYPE_I8:
+ out << indent() << "$output .= pack('c', $" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_I16:
+ out << indent() << "$output .= pack('n', $" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_I32:
+ out << indent() << "$output .= pack('N', $" << name << ");" << endl;
+ break;
+ case t_base_type::TYPE_I64:
+ out << indent() << "$output .= pack('N2', $" << name << " >> 32, $" << name
+ << " & 0xFFFFFFFF);" << endl;
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << indent() << "$output .= strrev(pack('d', $" << name << "));" << endl;
+ break;
+ default:
+ throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << indent() << "$output .= pack('N', $" << name << ");" << endl;
+ }
+ } else {
+
+ indent(out) << "$xfer += $output->";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ out << "writeString($" << name << ");";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool($" << name << ");";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte($" << name << ");";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16($" << name << ");";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32($" << name << ");";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64($" << name << ");";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble($" << name << ");";
+ break;
+ default:
+ throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32($" << name << ");";
+ }
+ out << endl;
+ }
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_php_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << "$xfer += $" << prefix << "->write($output);" << endl;
+}
+
+/**
+ * Writes out a container
+ */
+void t_php_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ if (ttype->is_map()) {
+ if (binary_inline_) {
+ out << indent() << "$output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ");" << endl << indent() << "$output .= pack('c', "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ");" << endl << indent()
+ << "$output .= strrev(pack('l', count($" << prefix << ")));" << endl;
+ } else {
+ indent(out) << "$output->writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type())
+ << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "count($" << prefix << "));" << endl;
+ }
+ } else if (ttype->is_set()) {
+ if (binary_inline_) {
+ out << indent() << "$output .= pack('c', " << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ");" << endl << indent() << "$output .= strrev(pack('l', count($" << prefix << ")));"
+ << endl;
+
+ } else {
+ indent(out) << "$output->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type())
+ << ", "
+ << "count($" << prefix << "));" << endl;
+ }
+ } else if (ttype->is_list()) {
+ if (binary_inline_) {
+ out << indent() << "$output .= pack('c', " << type_to_enum(((t_list*)ttype)->get_elem_type())
+ << ");" << endl << indent() << "$output .= strrev(pack('l', count($" << prefix << ")));"
+ << endl;
+
+ } else {
+ indent(out) << "$output->writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
+ << ", "
+ << "count($" << prefix << "));" << endl;
+ }
+ }
+
+ if (ttype->is_map()) {
+ string kiter = tmp("kiter");
+ string viter = tmp("viter");
+ indent(out) << "foreach ($" << prefix << " as "
+ << "$" << kiter << " => $" << viter << ") {" << endl;
+ indent_up();
+ generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+ scope_down(out);
+ } else if (ttype->is_set()) {
+ string iter = tmp("iter");
+ string iter_val = tmp("iter");
+ indent(out) << "foreach ($" << prefix << " as $" << iter << " => $" << iter_val << ") {" << endl;
+ indent_up();
+
+ t_type* elem_type = ((t_set*)ttype)->get_elem_type();
+ if(php_is_scalar(elem_type)) {
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ } else {
+ generate_serialize_set_element(out, (t_set*)ttype, iter_val);
+ }
+ scope_down(out);
+ } else if (ttype->is_list()) {
+ string iter = tmp("iter");
+ indent(out) << "foreach ($" << prefix << " as $" << iter << ") {" << endl;
+ indent_up();
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ scope_down(out);
+ }
+
+ if (!binary_inline_) {
+ if (ttype->is_map()) {
+ indent(out) << "$output->writeMapEnd();" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "$output->writeSetEnd();" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "$output->writeListEnd();" << endl;
+ }
+ }
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_php_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), kiter);
+ generate_serialize_field(out, &kfield, "");
+
+ t_field vfield(tmap->get_val_type(), viter);
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_php_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_php_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Emits a PHPDoc comment for the given contents
+ */
+void t_php_generator::generate_php_docstring_comment(ostream& out, string contents) {
+ generate_docstring_comment(out, "/**\n", " * ", contents, " */\n");
+}
+
+/**
+ * Emits a PHPDoc comment if the provided object has a doc in Thrift
+ */
+void t_php_generator::generate_php_doc(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_php_docstring_comment(out, tdoc->get_doc());
+ }
+}
+
+/**
+ * Emits a PHPDoc comment for a field
+ */
+void t_php_generator::generate_php_doc(ostream& out, t_field* field) {
+ stringstream ss;
+
+ // prepend free-style doc if available
+ if (field->has_doc()) {
+ ss << field->get_doc() << endl;
+ }
+
+ // append @var tag
+ t_type* type = get_true_type(field->get_type());
+ ss << "@var " << type_to_phpdoc(type) << endl;
+
+ generate_php_docstring_comment(out, ss.str());
+}
+
+/**
+ * Emits a PHPDoc comment for a function
+ */
+void t_php_generator::generate_php_doc(ostream& out, t_function* function) {
+ stringstream ss;
+ if (function->has_doc()) {
+ ss << function->get_doc() << endl;
+ }
+
+ // generate parameter types doc
+ const vector<t_field*>& args = function->get_arglist()->get_members();
+ vector<t_field*>::const_iterator a_iter;
+ for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
+ t_field* arg = *a_iter;
+ ss << "@param " << type_to_phpdoc(arg->get_type()) << " $" << arg->get_name();
+ if (arg->has_doc()) {
+ ss << " " << arg->get_doc();
+ }
+ ss << endl;
+ }
+
+ // generate return type doc
+ t_type* ret_type = function->get_returntype();
+ if (!ret_type->is_void() || ret_type->has_doc()) {
+ ss << "@return " << type_to_phpdoc(ret_type);
+ if (ret_type->has_doc()) {
+ ss << " " << ret_type->get_doc();
+ }
+ ss << endl;
+ }
+
+ // generate exceptions doc
+ const vector<t_field*>& excs = function->get_xceptions()->get_members();
+ vector<t_field*>::const_iterator e_iter;
+ for (e_iter = excs.begin(); e_iter != excs.end(); ++e_iter) {
+ t_field* exc = *e_iter;
+ ss << "@throws " << type_to_phpdoc(exc->get_type());
+ if (exc->has_doc()) {
+ ss << " " << exc->get_doc();
+ }
+ ss << endl;
+ }
+
+ generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n");
+}
+
+/**
+ * Declares a field, which may include initialization as necessary.
+ *
+ * @param ttype The type
+ */
+string t_php_generator::declare_field(t_field* tfield, bool init, bool obj) {
+ string result = "$" + tfield->get_name();
+ if (init) {
+ t_type* type = get_true_type(tfield->get_type());
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ break;
+ case t_base_type::TYPE_STRING:
+ result += " = ''";
+ break;
+ case t_base_type::TYPE_BOOL:
+ result += " = false";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ result += " = 0";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ result += " = 0.0";
+ break;
+ default:
+ throw "compiler error: no PHP initializer for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ result += " = 0";
+ } else if (type->is_container()) {
+ result += " = array()";
+ } else if (type->is_struct() || type->is_xception()) {
+ if (obj) {
+ result += " = new " + php_namespace(type->get_program()) + type->get_name() + "()";
+ } else {
+ result += " = null";
+ }
+ }
+ }
+ return result + ";";
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_php_generator::function_signature(t_function* tfunction, string prefix) {
+ return prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")";
+}
+
+/**
+ * Renders a field list
+ */
+string t_php_generator::argument_list(t_struct* tstruct, bool addTypeHints) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+
+ t_type* type = (*f_iter)->get_type();
+
+ // Set type name
+ if (addTypeHints) {
+ if (type->is_struct()) {
+ string className = php_namespace(type->get_program())
+ + php_namespace_directory("Definition", false)
+ + classify(type->get_name());
+
+ result += className + " ";
+ } else if (type->is_container()) {
+ result += "array ";
+ }
+ }
+
+ result += "$" + (*f_iter)->get_name();
+ }
+ return result;
+}
+
+/**
+ * Gets a typecast string for a particular type.
+ */
+string t_php_generator::type_to_cast(t_type* type) {
+ if (type->is_base_type()) {
+ t_base_type* btype = (t_base_type*)type;
+ switch (btype->get_base()) {
+ case t_base_type::TYPE_BOOL:
+ return "(bool)";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "(int)";
+ case t_base_type::TYPE_DOUBLE:
+ return "(double)";
+ case t_base_type::TYPE_STRING:
+ return "(string)";
+ default:
+ return "";
+ }
+ } else if (type->is_enum()) {
+ return "(int)";
+ }
+ return "";
+}
+
+/**
+ * Converts the parse type to a C++ enum string for the given type.
+ */
+string t_php_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType::STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType::BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType::BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType::I16";
+ case t_base_type::TYPE_I32:
+ return "TType::I32";
+ case t_base_type::TYPE_I64:
+ return "TType::I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType::DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType::I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType::STRUCT";
+ } else if (type->is_map()) {
+ return "TType::MAP";
+ } else if (type->is_set()) {
+ return "TType::SET";
+ } else if (type->is_list()) {
+ return "TType::LST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/**
+ * Converts the parse type to a PHPDoc string for the given type.
+ */
+string t_php_generator::type_to_phpdoc(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ return "string";
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "int";
+ case t_base_type::TYPE_I16:
+ return "int";
+ case t_base_type::TYPE_I32:
+ return "int";
+ case t_base_type::TYPE_I64:
+ return "int";
+ case t_base_type::TYPE_DOUBLE:
+ return "double";
+ }
+ } else if (type->is_enum()) {
+ return "int";
+ } else if (type->is_struct() || type->is_xception()) {
+ return php_namespace(type->get_program()) + type->get_name();
+ } else if (type->is_map()) {
+ return "array";
+ } else if (type->is_set()) {
+ t_set* tset = static_cast<t_set*>(type);
+ t_type* t_elem = tset->get_elem_type();
+ if (t_elem->is_container()) {
+ return "(" + type_to_phpdoc(t_elem) + ")[]";
+ } else {
+ return type_to_phpdoc(t_elem) + "[]";
+ }
+ } else if (type->is_list()) {
+ t_list* tlist = static_cast<t_list*>(type);
+ t_type* t_elem = tlist->get_elem_type();
+ if (t_elem->is_container()) {
+ return "(" + type_to_phpdoc(t_elem) + ")[]";
+ } else {
+ return type_to_phpdoc(t_elem) + "[]";
+ }
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ php,
+ "PHP",
+ " inlined: Generate PHP inlined files\n"
+ " server: Generate PHP server stubs\n"
+ " oop: Generate PHP with object oriented subclasses\n"
+ " classmap: Generate old-style PHP files (use classmap autoloading)\n"
+ " rest: Generate PHP REST processors\n"
+ " nsglobal=NAME: Set global namespace\n"
+ " validate: Generate PHP validator methods\n"
+ " json: Generate JsonSerializable classes (requires PHP >= 5.4)\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc
new file mode 100644
index 000000000..982bca145
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc
@@ -0,0 +1,2781 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <vector>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+#include <algorithm>
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Python code generator.
+ *
+ */
+class t_py_generator : public t_generator {
+public:
+ t_py_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator (program) {
+ std::map<std::string, std::string>::const_iterator iter;
+
+ gen_newstyle_ = true;
+ gen_utf8strings_ = true;
+ gen_dynbase_ = false;
+ gen_slots_ = false;
+ gen_tornado_ = false;
+ gen_zope_interface_ = false;
+ gen_twisted_ = false;
+ gen_dynamic_ = false;
+ coding_ = "";
+ gen_dynbaseclass_ = "";
+ gen_dynbaseclass_exc_ = "";
+ gen_dynbaseclass_frozen_ = "";
+ import_dynbase_ = "";
+ package_prefix_ = "";
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("new_style") == 0) {
+ pwarning(0, "new_style is enabled by default, so the option will be removed in the near future.\n");
+ } else if( iter->first.compare("old_style") == 0) {
+ gen_newstyle_ = false;
+ pwarning(0, "old_style is deprecated and may be removed in the future.\n");
+ } else if( iter->first.compare("utf8strings") == 0) {
+ pwarning(0, "utf8strings is enabled by default, so the option will be removed in the near future.\n");
+ } else if( iter->first.compare("no_utf8strings") == 0) {
+ gen_utf8strings_ = false;
+ } else if( iter->first.compare("slots") == 0) {
+ gen_slots_ = true;
+ } else if( iter->first.compare("package_prefix") == 0) {
+ package_prefix_ = iter->second;
+ } else if( iter->first.compare("dynamic") == 0) {
+ gen_dynamic_ = true;
+ gen_newstyle_ = false; // dynamic is newstyle
+ if( gen_dynbaseclass_.empty()) {
+ gen_dynbaseclass_ = "TBase";
+ }
+ if( gen_dynbaseclass_frozen_.empty()) {
+ gen_dynbaseclass_frozen_ = "TFrozenBase";
+ }
+ if( gen_dynbaseclass_exc_.empty()) {
+ gen_dynbaseclass_exc_ = "TExceptionBase";
+ }
+ if( import_dynbase_.empty()) {
+ import_dynbase_ = "from thrift.protocol.TBase import TBase, TFrozenBase, TExceptionBase, TTransport\n";
+ }
+ } else if( iter->first.compare("dynbase") == 0) {
+ gen_dynbase_ = true;
+ gen_dynbaseclass_ = (iter->second);
+ } else if( iter->first.compare("dynfrozen") == 0) {
+ gen_dynbaseclass_frozen_ = (iter->second);
+ } else if( iter->first.compare("dynexc") == 0) {
+ gen_dynbaseclass_exc_ = (iter->second);
+ } else if( iter->first.compare("dynimport") == 0) {
+ gen_dynbase_ = true;
+ import_dynbase_ = (iter->second);
+ } else if( iter->first.compare("zope.interface") == 0) {
+ gen_zope_interface_ = true;
+ } else if( iter->first.compare("twisted") == 0) {
+ gen_twisted_ = true;
+ gen_zope_interface_ = true;
+ } else if( iter->first.compare("tornado") == 0) {
+ gen_tornado_ = true;
+ } else if( iter->first.compare("coding") == 0) {
+ coding_ = iter->second;
+ } else {
+ throw "unknown option py:" + iter->first;
+ }
+ }
+
+ if (gen_twisted_ && gen_tornado_) {
+ throw "at most one of 'twisted' and 'tornado' are allowed";
+ }
+
+ copy_options_ = option_string;
+
+ if (gen_twisted_) {
+ out_dir_base_ = "gen-py.twisted";
+ } else if (gen_tornado_) {
+ out_dir_base_ = "gen-py.tornado";
+ } else {
+ out_dir_base_ = "gen-py";
+ }
+ }
+
+ std::string indent_str() const override {
+ return " ";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_forward_declaration(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_py_struct(t_struct* tstruct, bool is_exception);
+ void generate_py_thrift_spec(std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_py_struct_definition(std::ostream& out,
+ t_struct* tstruct,
+ bool is_xception = false);
+ void generate_py_struct_reader(std::ostream& out, t_struct* tstruct);
+ void generate_py_struct_writer(std::ostream& out, t_struct* tstruct);
+ void generate_py_struct_required_validator(std::ostream& out, t_struct* tstruct);
+ void generate_py_function_helpers(t_function* tfunction);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_remote(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(std::ostream& out,
+ t_field* tfield,
+ std::string prefix = "");
+
+ void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(std::ostream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(std::ostream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
+
+ void generate_python_docstring(std::ostream& out, t_struct* tstruct);
+
+ void generate_python_docstring(std::ostream& out, t_function* tfunction);
+
+ void generate_python_docstring(std::ostream& out,
+ t_doc* tdoc,
+ t_struct* tstruct,
+ const char* subheader);
+
+ void generate_python_docstring(std::ostream& out, t_doc* tdoc);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string py_autogen_comment();
+ std::string py_imports();
+ std::string render_includes();
+ std::string declare_argument(t_field* tfield);
+ std::string render_field_default_value(t_field* tfield);
+ std::string type_name(t_type* ttype);
+ std::string function_signature(t_function* tfunction, bool interface = false);
+ std::string argument_list(t_struct* tstruct,
+ std::vector<std::string>* pre = NULL,
+ std::vector<std::string>* post = NULL);
+ std::string type_to_enum(t_type* ttype);
+ std::string type_to_spec_args(t_type* ttype);
+
+ static bool is_valid_namespace(const std::string& sub_namespace) {
+ return sub_namespace == "twisted";
+ }
+
+ static std::string get_real_py_module(const t_program* program, bool gen_twisted, std::string package_dir="") {
+ if (gen_twisted) {
+ std::string twisted_module = program->get_namespace("py.twisted");
+ if (!twisted_module.empty()) {
+ return twisted_module;
+ }
+ }
+
+ std::string real_module = program->get_namespace("py");
+ if (real_module.empty()) {
+ return program->get_name();
+ }
+ return package_dir + real_module;
+ }
+
+ static bool is_immutable(t_type* ttype) {
+ return ttype->annotations_.find("python.immutable") != ttype->annotations_.end();
+ }
+
+private:
+
+ /**
+ * True if we should generate new-style classes.
+ */
+ bool gen_newstyle_;
+
+ /**
+ * True if we should generate dynamic style classes.
+ */
+ bool gen_dynamic_;
+
+ bool gen_dynbase_;
+ std::string gen_dynbaseclass_;
+ std::string gen_dynbaseclass_frozen_;
+ std::string gen_dynbaseclass_exc_;
+
+ std::string import_dynbase_;
+
+ bool gen_slots_;
+
+ std::string copy_options_;
+
+ /**
+ * True if we should generate code for use with zope.interface.
+ */
+ bool gen_zope_interface_;
+
+ /**
+ * True if we should generate Twisted-friendly RPC services.
+ */
+ bool gen_twisted_;
+
+ /**
+ * True if we should generate code for use with Tornado
+ */
+ bool gen_tornado_;
+
+ /**
+ * True if strings should be encoded using utf-8.
+ */
+ bool gen_utf8strings_;
+
+ /**
+ * specify generated file encoding
+ * eg. # -*- coding: utf-8 -*-
+ */
+ string coding_;
+
+ string package_prefix_;
+
+ /**
+ * File streams
+ */
+
+ ofstream_with_content_based_conditional_update f_types_;
+ ofstream_with_content_based_conditional_update f_consts_;
+ ofstream_with_content_based_conditional_update f_service_;
+
+ std::string package_dir_;
+ std::string module_;
+
+protected:
+ std::set<std::string> lang_keywords() const override {
+ std::string keywords[] = { "False", "None", "True", "and", "as", "assert", "break", "class",
+ "continue", "def", "del", "elif", "else", "except", "exec", "finally", "for", "from",
+ "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "print",
+ "raise", "return", "try", "while", "with", "yield" };
+ return std::set<std::string>(keywords, keywords + sizeof(keywords)/sizeof(keywords[0]) );
+ }
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_py_generator::init_generator() {
+ // Make output directory
+ string module = get_real_py_module(program_, gen_twisted_);
+ package_dir_ = get_out_dir();
+ module_ = module;
+ while (true) {
+ // TODO: Do better error checking here.
+ MKDIR(package_dir_.c_str());
+ std::ofstream init_py((package_dir_ + "/__init__.py").c_str(), std::ios_base::app);
+ init_py.close();
+ if (module.empty()) {
+ break;
+ }
+ string::size_type pos = module.find('.');
+ if (pos == string::npos) {
+ package_dir_ += "/";
+ package_dir_ += module;
+ module.clear();
+ } else {
+ package_dir_ += "/";
+ package_dir_ += module.substr(0, pos);
+ module.erase(0, pos + 1);
+ }
+ }
+
+ // Make output file
+ string f_types_name = package_dir_ + "/" + "ttypes.py";
+ f_types_.open(f_types_name.c_str());
+
+ string f_consts_name = package_dir_ + "/" + "constants.py";
+ f_consts_.open(f_consts_name.c_str());
+
+ string f_init_name = package_dir_ + "/__init__.py";
+ ofstream_with_content_based_conditional_update f_init;
+ f_init.open(f_init_name.c_str());
+ f_init << "__all__ = ['ttypes', 'constants'";
+ vector<t_service*> services = program_->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ f_init << ", '" << (*sv_iter)->get_name() << "'";
+ }
+ f_init << "]" << endl;
+ f_init.close();
+
+ // Print header
+ f_types_ << py_autogen_comment() << endl
+ << py_imports() << endl
+ << render_includes() << endl
+ << "from thrift.transport import TTransport" << endl
+ << import_dynbase_;
+
+ f_types_ << "all_structs = []" << endl;
+
+ f_consts_ <<
+ py_autogen_comment() << endl <<
+ py_imports() << endl <<
+ "from .ttypes import *" << endl;
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_py_generator::render_includes() {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ for (auto include : includes) {
+ result += "import " + get_real_py_module(include, gen_twisted_, package_prefix_) + ".ttypes\n";
+ }
+ return result;
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_py_generator::py_autogen_comment() {
+ string coding;
+ if (!coding_.empty()) {
+ coding = "# -*- coding: " + coding_ + " -*-\n";
+ }
+ return coding + std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "#\n"
+ + "# options string: " + copy_options_ + "\n" + "#\n";
+}
+
+/**
+ * Prints standard thrift imports
+ */
+string t_py_generator::py_imports() {
+ ostringstream ss;
+ ss << "from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, "
+ "TApplicationException"
+ << endl
+ << "from thrift.protocol.TProtocol import TProtocolException"
+ << endl
+ << "from thrift.TRecursive import fix_spec"
+ << endl;
+
+ if (gen_utf8strings_) {
+ ss << endl << "import sys";
+ }
+ return ss.str();
+}
+
+/**
+ * Closes the type files
+ */
+void t_py_generator::close_generator() {
+
+ // Fix thrift_spec definitions for recursive structs.
+ f_types_ << "fix_spec(all_structs)" << endl;
+ f_types_ << "del all_structs" << endl;
+
+ // Close types file
+ f_types_.close();
+ f_consts_.close();
+}
+
+/**
+ * Generates a typedef. This is not done in Python, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_py_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Generates code for an enumerated type. Done using a class to scope
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_py_generator::generate_enum(t_enum* tenum) {
+ std::ostringstream to_string_mapping, from_string_mapping;
+
+ f_types_ << endl << endl << "class " << tenum->get_name() << (gen_newstyle_ ? "(object)" : "")
+ << (gen_dynamic_ ? "(" + gen_dynbaseclass_ + ")" : "") << ":" << endl;
+ indent_up();
+ generate_python_docstring(f_types_, tenum);
+
+ to_string_mapping << indent() << "_VALUES_TO_NAMES = {" << endl;
+ from_string_mapping << indent() << "_NAMES_TO_VALUES = {" << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ indent(f_types_) << (*c_iter)->get_name() << " = " << value << endl;
+
+ // Dictionaries to/from string names of enums
+ to_string_mapping << indent() << indent() << value << ": \""
+ << escape_string((*c_iter)->get_name()) << "\"," << endl;
+ from_string_mapping << indent() << indent() << '"' << escape_string((*c_iter)->get_name())
+ << "\": " << value << ',' << endl;
+ }
+ to_string_mapping << indent() << "}" << endl;
+ from_string_mapping << indent() << "}" << endl;
+
+ indent_down();
+ f_types_ << endl;
+ f_types_ << to_string_mapping.str() << endl << from_string_mapping.str();
+}
+
+/**
+ * Generate a constant value
+ */
+void t_py_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ indent(f_consts_) << name << " = " << render_const_value(type, value);
+ f_consts_ << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_py_generator::render_const_value(t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream out;
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ if (((t_base_type*)type)->is_binary()) {
+ out << 'b';
+ }
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "True" : "False");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << "float(" << value->get_integer() << ")";
+ } else {
+ out << emit_double_as_string(value->get_double());
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << type_name(type) << "(**{" << endl;
+ indent_up();
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ indent(out) << render_const_value(g_type_string, v_iter->first) << ": "
+ << render_const_value(field_type, v_iter->second) << "," << endl;
+ }
+ indent_down();
+ indent(out) << "})";
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ if (is_immutable(type)) {
+ out << "TFrozenDict(";
+ }
+ out << "{" << endl;
+ indent_up();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ indent(out) << render_const_value(ktype, v_iter->first) << ": "
+ << render_const_value(vtype, v_iter->second) << "," << endl;
+ }
+ indent_down();
+ indent(out) << "}";
+ if (is_immutable(type)) {
+ out << ")";
+ }
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ if (type->is_set()) {
+ if (is_immutable(type)) {
+ out << "frozen";
+ }
+ out << "set(";
+ }
+ if (is_immutable(type) || type->is_set()) {
+ out << "(" << endl;
+ } else {
+ out << "[" << endl;
+ }
+ indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ indent(out) << render_const_value(etype, *v_iter) << "," << endl;
+ }
+ indent_down();
+ if (is_immutable(type) || type->is_set()) {
+ indent(out) << ")";
+ } else {
+ indent(out) << "]";
+ }
+ if (type->is_set()) {
+ out << ")";
+ }
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+
+ return out.str();
+}
+
+/**
+ * Generates the "forward declarations" for python structs.
+ * These are actually full class definitions so that calls to generate_struct
+ * can add the thrift_spec field. This is needed so that all thrift_spec
+ * definitions are grouped at the end of the file to enable co-recursive structs.
+ */
+void t_py_generator::generate_forward_declaration(t_struct* tstruct) {
+ generate_py_struct(tstruct, tstruct->is_xception());
+}
+
+/**
+ * Generates a python struct
+ */
+void t_py_generator::generate_struct(t_struct* tstruct) {
+ generate_py_thrift_spec(f_types_, tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_py_generator::generate_xception(t_struct* txception) {
+ generate_py_thrift_spec(f_types_, txception, true);
+}
+
+/**
+ * Generates a python struct
+ */
+void t_py_generator::generate_py_struct(t_struct* tstruct, bool is_exception) {
+ generate_py_struct_definition(f_types_, tstruct, is_exception);
+}
+
+
+/**
+ * Generate the thrift_spec for a struct
+ * For example,
+ * all_structs.append(Recursive)
+ * Recursive.thrift_spec = (
+ * None, # 0
+ * (1, TType.LIST, 'Children', (TType.STRUCT, (Recursive, None), False), None, ), # 1
+ * )
+ */
+void t_py_generator::generate_py_thrift_spec(ostream& out,
+ t_struct* tstruct,
+ bool /*is_exception*/) {
+ const vector<t_field*>& sorted_members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ // Add struct definition to list so thrift_spec can be fixed for recursive structures.
+ indent(out) << "all_structs.append(" << tstruct->get_name() << ")" << endl;
+
+ if (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) {
+ indent(out) << tstruct->get_name() << ".thrift_spec = (" << endl;
+ indent_up();
+
+ int sorted_keys_pos = 0;
+ for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) {
+
+ for (; sorted_keys_pos != (*m_iter)->get_key(); sorted_keys_pos++) {
+ indent(out) << "None, # " << sorted_keys_pos << endl;
+ }
+
+ indent(out) << "(" << (*m_iter)->get_key() << ", " << type_to_enum((*m_iter)->get_type())
+ << ", "
+ << "'" << (*m_iter)->get_name() << "'"
+ << ", " << type_to_spec_args((*m_iter)->get_type()) << ", "
+ << render_field_default_value(*m_iter) << ", "
+ << "),"
+ << " # " << sorted_keys_pos << endl;
+
+ sorted_keys_pos++;
+ }
+
+ indent_down();
+ indent(out) << ")" << endl;
+ } else {
+ indent(out) << tstruct->get_name() << ".thrift_spec = ()" << endl;
+ }
+}
+
+/**
+ * Generates a struct definition for a thrift data type.
+ *
+ * @param tstruct The struct definition
+ */
+void t_py_generator::generate_py_struct_definition(ostream& out,
+ t_struct* tstruct,
+ bool is_exception) {
+ const vector<t_field*>& members = tstruct->get_members();
+ const vector<t_field*>& sorted_members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ out << endl << endl << "class " << tstruct->get_name();
+ if (is_exception) {
+ if (gen_dynamic_) {
+ out << "(" << gen_dynbaseclass_exc_ << ")";
+ } else {
+ out << "(TException)";
+ }
+ } else if (gen_dynamic_) {
+ if (is_immutable(tstruct)) {
+ out << "(" << gen_dynbaseclass_frozen_ << ")";
+ } else {
+ out << "(" << gen_dynbaseclass_ << ")";
+ }
+ } else if (gen_newstyle_) {
+ out << "(object)";
+ }
+ out << ":" << endl;
+ indent_up();
+ generate_python_docstring(out, tstruct);
+
+ out << endl;
+
+ /*
+ Here we generate the structure specification for the fastbinary codec.
+ These specifications have the following structure:
+ thrift_spec -> tuple of item_spec
+ item_spec -> None | (tag, type_enum, name, spec_args, default)
+ tag -> integer
+ type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ...
+ name -> string_literal
+ default -> None # Handled by __init__
+ spec_args -> None # For simple types
+ | (type_enum, spec_args) # Value type for list/set
+ | (type_enum, spec_args, type_enum, spec_args)
+ # Key and value for map
+ | (class_name, spec_args_ptr) # For struct/exception
+ class_name -> identifier # Basically a pointer to the class
+ spec_args_ptr -> expression # just class_name.spec_args
+
+ TODO(dreiss): Consider making this work for structs with negative tags.
+ */
+
+ if (gen_slots_) {
+ indent(out) << "__slots__ = (" << endl;
+ indent_up();
+ for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) {
+ indent(out) << "'" << (*m_iter)->get_name() << "'," << endl;
+ }
+ indent_down();
+ indent(out) << ")" << endl << endl;
+ }
+
+ // TODO(dreiss): Look into generating an empty tuple instead of None
+ // for structures with no members.
+ // TODO(dreiss): Test encoding of structs where some inner structs
+ // don't have thrift_spec.
+
+ if (members.size() > 0) {
+ out << endl;
+ out << indent() << "def __init__(self,";
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << " " << declare_argument(*m_iter) << ",";
+ }
+ out << "):" << endl;
+
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ // Initialize fields
+ t_type* type = (*m_iter)->get_type();
+ if (!type->is_base_type() && !type->is_enum() && (*m_iter)->get_value() != NULL) {
+ indent(out) << "if " << (*m_iter)->get_name() << " is "
+ << "self.thrift_spec[" << (*m_iter)->get_key() << "][4]:" << endl;
+ indent_up();
+ indent(out) << (*m_iter)->get_name() << " = " << render_field_default_value(*m_iter)
+ << endl;
+ indent_down();
+ }
+
+ if (is_immutable(tstruct)) {
+ if (gen_newstyle_ || gen_dynamic_) {
+ indent(out) << "super(" << tstruct->get_name() << ", self).__setattr__('"
+ << (*m_iter)->get_name() << "', " << (*m_iter)->get_name() << ")" << endl;
+ } else {
+ indent(out) << "self.__dict__['" << (*m_iter)->get_name()
+ << "'] = " << (*m_iter)->get_name() << endl;
+ }
+ } else {
+ indent(out) << "self." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << endl;
+ }
+ }
+
+ indent_down();
+ }
+
+ if (is_immutable(tstruct)) {
+ out << endl;
+ out << indent() << "def __setattr__(self, *args):" << endl
+ << indent() << indent_str() << "raise TypeError(\"can't modify immutable instance\")" << endl
+ << endl;
+ out << indent() << "def __delattr__(self, *args):" << endl
+ << indent() << indent_str() << "raise TypeError(\"can't modify immutable instance\")" << endl
+ << endl;
+
+ // Hash all of the members in order, and also hash in the class
+ // to avoid collisions for stuff like single-field structures.
+ out << indent() << "def __hash__(self):" << endl
+ << indent() << indent_str() << "return hash(self.__class__) ^ hash((";
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << "self." << (*m_iter)->get_name() << ", ";
+ }
+
+ out << "))" << endl;
+ }
+
+ if (!gen_dynamic_) {
+ out << endl;
+ generate_py_struct_reader(out, tstruct);
+ generate_py_struct_writer(out, tstruct);
+ }
+
+ // For exceptions only, generate a __str__ method. This is
+ // because when raised exceptions are printed to the console, __repr__
+ // isn't used. See python bug #5882
+ if (is_exception) {
+ out << endl;
+ out << indent() << "def __str__(self):" << endl
+ << indent() << indent_str() << "return repr(self)" << endl;
+ }
+
+ if (!gen_slots_) {
+ out << endl;
+ // Printing utilities so that on the command line thrift
+ // structs look pretty like dictionaries
+ indent(out) << "def __repr__(self):" << endl;
+ indent_up();
+ out << indent() << "L = ['%s=%r' % (key, value)" << endl
+ << indent() << " for key, value in self.__dict__.items()]" << endl
+ << indent() << "return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl
+ << endl;
+ indent_down();
+
+ // Equality and inequality methods that compare by value
+ out << indent() << "def __eq__(self, other):" << endl;
+ indent_up();
+ out << indent() << "return isinstance(other, self.__class__) and "
+ "self.__dict__ == other.__dict__" << endl;
+ indent_down();
+ out << endl;
+
+ out << indent() << "def __ne__(self, other):" << endl;
+ indent_up();
+
+ out << indent() << "return not (self == other)" << endl;
+ indent_down();
+ } else if (!gen_dynamic_) {
+ out << endl;
+ // no base class available to implement __eq__ and __repr__ and __ne__ for us
+ // so we must provide one that uses __slots__
+ indent(out) << "def __repr__(self):" << endl;
+ indent_up();
+ out << indent() << "L = ['%s=%r' % (key, getattr(self, key))" << endl
+ << indent() << " for key in self.__slots__]" << endl
+ << indent() << "return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl
+ << endl;
+ indent_down();
+
+ // Equality method that compares each attribute by value and type, walking __slots__
+ out << indent() << "def __eq__(self, other):" << endl;
+ indent_up();
+ out << indent() << "if not isinstance(other, self.__class__):" << endl
+ << indent() << indent_str() << "return False" << endl
+ << indent() << "for attr in self.__slots__:" << endl
+ << indent() << indent_str() << "my_val = getattr(self, attr)" << endl
+ << indent() << indent_str() << "other_val = getattr(other, attr)" << endl
+ << indent() << indent_str() << "if my_val != other_val:" << endl
+ << indent() << indent_str() << indent_str() << "return False" << endl
+ << indent() << "return True" << endl
+ << endl;
+ indent_down();
+
+ out << indent() << "def __ne__(self, other):" << endl
+ << indent() << indent_str() << "return not (self == other)" << endl;
+ }
+ indent_down();
+}
+
+/**
+ * Generates the read method for a struct
+ */
+void t_py_generator::generate_py_struct_reader(ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (is_immutable(tstruct)) {
+ out << indent() << "@classmethod" << endl << indent() << "def read(cls, iprot):" << endl;
+ } else {
+ indent(out) << "def read(self, iprot):" << endl;
+ }
+ indent_up();
+
+ const char* id = is_immutable(tstruct) ? "cls" : "self";
+
+ indent(out) << "if iprot._fast_decode is not None "
+ "and isinstance(iprot.trans, TTransport.CReadableTransport) "
+ "and "
+ << id << ".thrift_spec is not None:" << endl;
+ indent_up();
+
+ if (is_immutable(tstruct)) {
+ indent(out) << "return iprot._fast_decode(None, iprot, [cls, cls.thrift_spec])" << endl;
+ } else {
+ indent(out) << "iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])" << endl;
+ indent(out) << "return" << endl;
+ }
+ indent_down();
+
+ indent(out) << "iprot.readStructBegin()" << endl;
+
+ if (is_immutable(tstruct)) {
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* tfield = *f_iter;
+ std::ostringstream result;
+ result << tfield->get_name() << " = ";
+ if (tfield->get_value() != NULL) {
+ result << render_field_default_value(tfield);
+ } else {
+ result << "None";
+ }
+ indent(out) << result.str() << endl;
+ }
+ }
+
+ // Loop over reading in fields
+ indent(out) << "while True:" << endl;
+ indent_up();
+
+ // Read beginning field marker
+ indent(out) << "(fname, ftype, fid) = iprot.readFieldBegin()" << endl;
+
+ // Check for field STOP marker and break
+ indent(out) << "if ftype == TType.STOP:" << endl;
+ indent_up();
+ indent(out) << "break" << endl;
+ indent_down();
+
+ // Switch statement on the field we are reading
+ bool first = true;
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ out << indent() << "if ";
+ } else {
+ out << indent() << "elif ";
+ }
+ out << "fid == " << (*f_iter)->get_key() << ":" << endl;
+ indent_up();
+ indent(out) << "if ftype == " << type_to_enum((*f_iter)->get_type()) << ":" << endl;
+ indent_up();
+ if (is_immutable(tstruct)) {
+ generate_deserialize_field(out, *f_iter);
+ } else {
+ generate_deserialize_field(out, *f_iter, "self.");
+ }
+ indent_down();
+ out << indent() << "else:" << endl << indent() << indent_str() << "iprot.skip(ftype)" << endl;
+ indent_down();
+ }
+
+ // In the default case we skip the field
+ out << indent() << "else:" << endl << indent() << indent_str() << "iprot.skip(ftype)" << endl;
+
+ // Read field end marker
+ indent(out) << "iprot.readFieldEnd()" << endl;
+
+ indent_down();
+
+ indent(out) << "iprot.readStructEnd()" << endl;
+
+ if (is_immutable(tstruct)) {
+ indent(out) << "return cls(" << endl;
+ indent_up();
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << (*f_iter)->get_name() << "=" << (*f_iter)->get_name() << "," << endl;
+ }
+ indent_down();
+ indent(out) << ")" << endl;
+ }
+
+ indent_down();
+ out << endl;
+}
+
+void t_py_generator::generate_py_struct_writer(ostream& out, t_struct* tstruct) {
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "def write(self, oprot):" << endl;
+ indent_up();
+
+ indent(out) << "if oprot._fast_encode is not None and self.thrift_spec is not None:" << endl;
+ indent_up();
+
+ indent(out)
+ << "oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec]))"
+ << endl;
+ indent(out) << "return" << endl;
+ indent_down();
+
+ indent(out) << "oprot.writeStructBegin('" << name << "')" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ // Write field header
+ indent(out) << "if self." << (*f_iter)->get_name() << " is not None:" << endl;
+ indent_up();
+ indent(out) << "oprot.writeFieldBegin("
+ << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type())
+ << ", " << (*f_iter)->get_key() << ")" << endl;
+
+ // Write field contents
+ generate_serialize_field(out, *f_iter, "self.");
+
+ // Write field closer
+ indent(out) << "oprot.writeFieldEnd()" << endl;
+
+ indent_down();
+ }
+
+ // Write the struct map
+ out << indent() << "oprot.writeFieldStop()" << endl << indent() << "oprot.writeStructEnd()"
+ << endl;
+
+ out << endl;
+
+ indent_down();
+ generate_py_struct_required_validator(out, tstruct);
+}
+
+void t_py_generator::generate_py_struct_required_validator(ostream& out, t_struct* tstruct) {
+ indent(out) << "def validate(self):" << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+
+ if (fields.size() > 0) {
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ if (field->get_req() == t_field::T_REQUIRED) {
+ indent(out) << "if self." << field->get_name() << " is None:" << endl;
+ indent(out) << indent_str() << "raise TProtocolException(message='Required field "
+ << field->get_name() << " is unset!')" << endl;
+ }
+ }
+ }
+
+ indent(out) << "return" << endl;
+ indent_down();
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_py_generator::generate_service(t_service* tservice) {
+ string f_service_name = package_dir_ + "/" + service_name_ + ".py";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << py_autogen_comment() << endl << py_imports() << endl;
+
+ if (tservice->get_extends() != NULL) {
+ f_service_ << "import "
+ << get_real_py_module(tservice->get_extends()->get_program(), gen_twisted_, package_prefix_) << "."
+ << tservice->get_extends()->get_name() << endl;
+ }
+
+ f_service_ << "import logging" << endl
+ << "from .ttypes import *" << endl
+ << "from thrift.Thrift import TProcessor" << endl
+ << "from thrift.transport import TTransport" << endl
+ << import_dynbase_;
+ if (gen_zope_interface_) {
+ f_service_ << "from zope.interface import Interface, implementer" << endl;
+ }
+
+ if (gen_twisted_) {
+ f_service_ << "from twisted.internet import defer" << endl
+ << "from thrift.transport import TTwisted" << endl;
+ } else if (gen_tornado_) {
+ f_service_ << "from tornado import gen" << endl;
+ f_service_ << "from tornado import concurrent" << endl;
+ }
+
+ f_service_ << "all_structs = []" << endl;
+
+ // Generate the three main parts of the service
+ generate_service_interface(tservice);
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+ generate_service_remote(tservice);
+
+ // Close service file
+ f_service_ << "fix_spec(all_structs)" << endl
+ << "del all_structs" << endl << endl;
+ f_service_.close();
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_py_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ f_service_ << endl << "# HELPER FUNCTIONS AND STRUCTURES" << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_py_struct_definition(f_service_, ts, false);
+ generate_py_thrift_spec(f_service_, ts, false);
+ generate_py_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_py_generator::generate_py_function_helpers(t_function* tfunction) {
+ if (!tfunction->is_oneway()) {
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+ generate_py_struct_definition(f_service_, &result, false);
+ generate_py_thrift_spec(f_service_, &result, false);
+ }
+}
+
+/**
+ * Generates a service interface definition.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_py_generator::generate_service_interface(t_service* tservice) {
+ string extends = "";
+ string extends_if = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_if = "(" + extends + ".Iface)";
+ } else {
+ if (gen_zope_interface_) {
+ extends_if = "(Interface)";
+ } else if (gen_newstyle_ || gen_dynamic_ || gen_tornado_) {
+ extends_if = "(object)";
+ }
+ }
+
+ f_service_ << endl << endl << "class Iface" << extends_if << ":" << endl;
+ indent_up();
+ generate_python_docstring(f_service_, tservice);
+ vector<t_function*> functions = tservice->get_functions();
+ if (functions.empty()) {
+ f_service_ << indent() << "pass" << endl;
+ } else {
+ vector<t_function*>::iterator f_iter;
+ bool first = true;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << endl;
+ }
+ f_service_ << indent() << "def " << function_signature(*f_iter, true) << ":" << endl;
+ indent_up();
+ generate_python_docstring(f_service_, (*f_iter));
+ f_service_ << indent() << "pass" << endl;
+ indent_down();
+ }
+ }
+
+ indent_down();
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_py_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ if (gen_zope_interface_) {
+ extends_client = "(" + extends + ".Client)";
+ } else {
+ extends_client = extends + ".Client, ";
+ }
+ } else {
+ if (gen_zope_interface_ && (gen_newstyle_ || gen_dynamic_)) {
+ extends_client = "(object)";
+ }
+ }
+
+ f_service_ << endl << endl;
+
+ if (gen_zope_interface_) {
+ f_service_ << "@implementer(Iface)" << endl
+ << "class Client" << extends_client << ":" << endl
+ << endl;
+ } else {
+ f_service_ << "class Client(" << extends_client << "Iface):" << endl;
+ }
+ indent_up();
+ generate_python_docstring(f_service_, tservice);
+
+ // Constructor function
+ if (gen_twisted_) {
+ f_service_ << indent() << "def __init__(self, transport, oprot_factory):" << endl;
+ } else if (gen_tornado_) {
+ f_service_ << indent()
+ << "def __init__(self, transport, iprot_factory, oprot_factory=None):" << endl;
+ } else {
+ f_service_ << indent() << "def __init__(self, iprot, oprot=None):" << endl;
+ }
+ indent_up();
+ if (extends.empty()) {
+ if (gen_twisted_) {
+ f_service_ << indent() << "self._transport = transport" << endl
+ << indent() << "self._oprot_factory = oprot_factory" << endl
+ << indent() << "self._seqid = 0" << endl
+ << indent() << "self._reqs = {}" << endl;
+ } else if (gen_tornado_) {
+ f_service_ << indent() << "self._transport = transport" << endl
+ << indent() << "self._iprot_factory = iprot_factory" << endl
+ << indent() << "self._oprot_factory = (oprot_factory if oprot_factory is not None"
+ << endl
+ << indent() << " else iprot_factory)" << endl
+ << indent() << "self._seqid = 0" << endl
+ << indent() << "self._reqs = {}" << endl
+ << indent() << "self._transport.io_loop.spawn_callback(self._start_receiving)"
+ << endl;
+ } else {
+ f_service_ << indent() << "self._iprot = self._oprot = iprot" << endl
+ << indent() << "if oprot is not None:" << endl
+ << indent() << indent_str() << "self._oprot = oprot" << endl
+ << indent() << "self._seqid = 0" << endl;
+ }
+ } else {
+ if (gen_twisted_) {
+ f_service_ << indent() << extends
+ << ".Client.__init__(self, transport, oprot_factory)" << endl;
+ } else if (gen_tornado_) {
+ f_service_ << indent() << extends
+ << ".Client.__init__(self, transport, iprot_factory, oprot_factory)" << endl;
+ } else {
+ f_service_ << indent() << extends << ".Client.__init__(self, iprot, oprot)" << endl;
+ }
+ }
+ indent_down();
+
+ if (gen_tornado_ && extends.empty()) {
+ f_service_ << endl <<
+ indent() << "@gen.engine" << endl <<
+ indent() << "def _start_receiving(self):" << endl;
+ indent_up();
+ indent(f_service_) << "while True:" << endl;
+ indent_up();
+ f_service_ << indent() << "try:" << endl
+ << indent() << indent_str() << "frame = yield self._transport.readFrame()" << endl
+ << indent() << "except TTransport.TTransportException as e:" << endl
+ << indent() << indent_str() << "for future in self._reqs.values():" << endl
+ << indent() << indent_str() << indent_str() << "future.set_exception(e)" << endl
+ << indent() << indent_str() << "self._reqs = {}" << endl
+ << indent() << indent_str() << "return" << endl
+ << indent() << "tr = TTransport.TMemoryBuffer(frame)" << endl
+ << indent() << "iprot = self._iprot_factory.getProtocol(tr)" << endl
+ << indent() << "(fname, mtype, rseqid) = iprot.readMessageBegin()" << endl
+ << indent() << "method = getattr(self, 'recv_' + fname)" << endl
+ << indent() << "future = self._reqs.pop(rseqid, None)" << endl
+ << indent() << "if not future:" << endl
+ << indent() << indent_str() << "# future has already been discarded" << endl
+ << indent() << indent_str() << "continue" << endl
+ << indent() << "try:" << endl
+ << indent() << indent_str() << "result = method(iprot, mtype, rseqid)" << endl
+ << indent() << "except Exception as e:" << endl
+ << indent() << indent_str() << "future.set_exception(e)" << endl
+ << indent() << "else:" << endl
+ << indent() << indent_str() << "future.set_result(result)" << endl;
+ indent_down();
+ indent_down();
+ }
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+
+ f_service_ << endl;
+ // Open function
+ indent(f_service_) << "def " << function_signature(*f_iter, false) << ":" << endl;
+ indent_up();
+ generate_python_docstring(f_service_, (*f_iter));
+ if (gen_twisted_) {
+ indent(f_service_) << "seqid = self._seqid = self._seqid + 1" << endl;
+ indent(f_service_) << "self._reqs[seqid] = defer.Deferred()" << endl << endl;
+ indent(f_service_) << "d = defer.maybeDeferred(self.send_" << funname;
+
+ } else if (gen_tornado_) {
+ indent(f_service_) << "self._seqid += 1" << endl;
+ if (!(*f_iter)->is_oneway()) {
+ indent(f_service_) << "future = self._reqs[self._seqid] = concurrent.Future()" << endl;
+ }
+ indent(f_service_) << "self.send_" << funname << "(";
+
+ } else {
+ indent(f_service_) << "self.send_" << funname << "(";
+ }
+
+ bool first = true;
+ if (gen_twisted_) {
+ // we need a leading comma if there are args, since it's called as maybeDeferred(funcname,
+ // arg)
+ first = false;
+ }
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << (*fld_iter)->get_name();
+ }
+
+ f_service_ << ")" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ if (gen_twisted_) {
+ // nothing. See the next block.
+ } else if (gen_tornado_) {
+ indent(f_service_) << "return future" << endl;
+ } else {
+ f_service_ << indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "self.recv_" << funname << "()" << endl;
+ }
+ }
+ indent_down();
+
+ if (gen_twisted_) {
+ // This block injects the body of the send_<> method for twisted (and a cb/eb pair)
+ indent_up();
+ indent(f_service_) << "d.addCallbacks(" << endl;
+
+ indent_up();
+ f_service_ << indent() << "callback=self.cb_send_" << funname << "," << endl << indent()
+ << "callbackArgs=(seqid,)," << endl << indent() << "errback=self.eb_send_"
+ << funname << "," << endl << indent() << "errbackArgs=(seqid,))" << endl;
+ indent_down();
+
+ indent(f_service_) << "return d" << endl;
+ indent_down();
+ f_service_ << endl;
+
+ indent(f_service_) << "def cb_send_" << funname << "(self, _, seqid):" << endl;
+ indent_up();
+ if ((*f_iter)->is_oneway()) {
+ // if one-way, fire the deferred & remove it from _reqs
+ f_service_ << indent() << "d = self._reqs.pop(seqid)" << endl << indent()
+ << "d.callback(None)" << endl << indent() << "return d" << endl;
+ } else {
+ f_service_ << indent() << "return self._reqs[seqid]" << endl;
+ }
+ indent_down();
+ f_service_ << endl;
+
+ // add an errback to fail the request if the call to send_<> raised an exception
+ indent(f_service_) << "def eb_send_" << funname << "(self, f, seqid):" << endl;
+ indent_up();
+ f_service_ << indent() << "d = self._reqs.pop(seqid)" << endl << indent() << "d.errback(f)"
+ << endl << indent() << "return d" << endl;
+ indent_down();
+ }
+
+ f_service_ << endl;
+ indent(f_service_) << "def send_" << function_signature(*f_iter, false) << ":" << endl;
+ indent_up();
+
+ std::string argsname = (*f_iter)->get_name() + "_args";
+ std::string messageType = (*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL";
+
+ // Serialize the request header
+ if (gen_twisted_ || gen_tornado_) {
+ f_service_ << indent() << "oprot = self._oprot_factory.getProtocol(self._transport)" << endl
+ << indent() << "oprot.writeMessageBegin('" << (*f_iter)->get_name() << "', "
+ << messageType << ", self._seqid)" << endl;
+ } else {
+ f_service_ << indent() << "self._oprot.writeMessageBegin('" << (*f_iter)->get_name() << "', "
+ << messageType << ", self._seqid)" << endl;
+ }
+
+ f_service_ << indent() << "args = " << argsname << "()" << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = "
+ << (*fld_iter)->get_name() << endl;
+ }
+
+ // Write to the stream
+ if (gen_twisted_ || gen_tornado_) {
+ f_service_ << indent() << "args.write(oprot)" << endl << indent() << "oprot.writeMessageEnd()"
+ << endl << indent() << "oprot.trans.flush()" << endl;
+ } else {
+ f_service_ << indent() << "args.write(self._oprot)" << endl << indent()
+ << "self._oprot.writeMessageEnd()" << endl << indent()
+ << "self._oprot.trans.flush()" << endl;
+ }
+
+ indent_down();
+
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultname = (*f_iter)->get_name() + "_result";
+ // Open function
+ f_service_ << endl;
+ if (gen_twisted_ || gen_tornado_) {
+ f_service_ << indent() << "def recv_" << (*f_iter)->get_name()
+ << "(self, iprot, mtype, rseqid):" << endl;
+ } else {
+ t_struct noargs(program_);
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ f_service_ << indent() << "def " << function_signature(&recv_function) << ":" << endl;
+ }
+ indent_up();
+
+ // TODO(mcslee): Validate message reply here, seq ids etc.
+
+ if (gen_twisted_) {
+ f_service_ << indent() << "d = self._reqs.pop(rseqid)" << endl;
+ } else if (gen_tornado_) {
+ } else {
+ f_service_ << indent() << "iprot = self._iprot" << endl << indent()
+ << "(fname, mtype, rseqid) = iprot.readMessageBegin()" << endl;
+ }
+
+ f_service_ << indent() << "if mtype == TMessageType.EXCEPTION:" << endl
+ << indent() << indent_str() << "x = TApplicationException()" << endl;
+
+ if (gen_twisted_) {
+ f_service_ << indent() << indent_str() << "x.read(iprot)" << endl << indent()
+ << indent_str() << "iprot.readMessageEnd()" << endl << indent() << indent_str() << "return d.errback(x)"
+ << endl << indent() << "result = " << resultname << "()" << endl << indent()
+ << "result.read(iprot)" << endl << indent() << "iprot.readMessageEnd()" << endl;
+ } else {
+ f_service_ << indent() << indent_str() << "x.read(iprot)" << endl << indent()
+ << indent_str() << "iprot.readMessageEnd()" << endl << indent() << indent_str() << "raise x" << endl
+ << indent() << "result = " << resultname << "()" << endl << indent()
+ << "result.read(iprot)" << endl << indent() << "iprot.readMessageEnd()" << endl;
+ }
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << indent() << "if result.success is not None:" << endl;
+ if (gen_twisted_) {
+ f_service_ << indent() << indent_str() << "return d.callback(result.success)" << endl;
+ } else {
+ f_service_ << indent() << indent_str() << "return result.success" << endl;
+ }
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ const string& xname = (*x_iter)->get_name();
+ f_service_ << indent() << "if result." << xname << " is not None:" << endl;
+ if (gen_twisted_) {
+ f_service_ << indent() << indent_str() << "return d.errback(result." << xname << ")"
+ << endl;
+ } else {
+ f_service_ << indent() << indent_str() << "raise result." << xname << "" << endl;
+ }
+ }
+
+ // Careful, only return _result if not a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ if (gen_twisted_) {
+ f_service_ << indent() << "return d.callback(None)" << endl;
+ } else {
+ f_service_ << indent() << "return" << endl;
+ }
+ } else {
+ if (gen_twisted_) {
+ f_service_
+ << indent()
+ << "return d.errback(TApplicationException(TApplicationException.MISSING_RESULT, \""
+ << (*f_iter)->get_name() << " failed: unknown result\"))" << endl;
+ } else {
+ f_service_ << indent()
+ << "raise TApplicationException(TApplicationException.MISSING_RESULT, \""
+ << (*f_iter)->get_name() << " failed: unknown result\")" << endl;
+ }
+ }
+
+ // Close function
+ indent_down();
+ }
+ }
+
+ indent_down();
+}
+
+/**
+ * Generates a command line tool for making remote requests
+ *
+ * @param tservice The service to generate a remote for.
+ */
+void t_py_generator::generate_service_remote(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ // Get all function from parents
+ t_service* parent = tservice->get_extends();
+ while (parent != NULL) {
+ vector<t_function*> p_functions = parent->get_functions();
+ functions.insert(functions.end(), p_functions.begin(), p_functions.end());
+ parent = parent->get_extends();
+ }
+ vector<t_function*>::iterator f_iter;
+
+ string f_remote_name = package_dir_ + "/" + service_name_ + "-remote";
+ ofstream_with_content_based_conditional_update f_remote;
+ f_remote.open(f_remote_name.c_str());
+
+ f_remote <<
+ "#!/usr/bin/env python" << endl <<
+ py_autogen_comment() << endl <<
+ "import sys" << endl <<
+ "import pprint" << endl <<
+ "if sys.version_info[0] > 2:" << endl <<
+ indent_str() << "from urllib.parse import urlparse" << endl <<
+ "else:" << endl <<
+ indent_str() << "from urlparse import urlparse" << endl <<
+ "from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient" << endl <<
+ "from thrift.protocol.TBinaryProtocol import TBinaryProtocol" << endl <<
+ endl;
+
+ f_remote <<
+ "from " << module_ << " import " << service_name_ << endl <<
+ "from " << module_ << ".ttypes import *" << endl <<
+ endl;
+
+ f_remote <<
+ "if len(sys.argv) <= 1 or sys.argv[1] == '--help':" << endl <<
+ indent_str() << "print('')" << endl <<
+ indent_str() << "print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]')" << endl <<
+ indent_str() << "print('')" << endl <<
+ indent_str() << "print('Functions:')" << endl;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_remote << indent_str() << "print(' " << (*f_iter)->get_returntype()->get_name() << " "
+ << (*f_iter)->get_name() << "(";
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const std::vector<t_field*>& args = arg_struct->get_members();
+ std::vector<t_field*>::size_type num_args = args.size();
+ bool first = true;
+ for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ f_remote << ", ";
+ }
+ f_remote << args[i]->get_type()->get_name() << " " << args[i]->get_name();
+ }
+ f_remote << ")')" << endl;
+ }
+ f_remote << indent_str() << "print('')" << endl << indent_str() << "sys.exit(0)" << endl << endl;
+
+ f_remote << "pp = pprint.PrettyPrinter(indent=2)" << endl
+ << "host = 'localhost'" << endl
+ << "port = 9090" << endl
+ << "uri = ''" << endl
+ << "framed = False" << endl
+ << "ssl = False" << endl
+ << "validate = True" << endl
+ << "ca_certs = None" << endl
+ << "keyfile = None" << endl
+ << "certfile = None" << endl
+ << "http = False" << endl
+ << "argi = 1" << endl
+ << endl
+ << "if sys.argv[argi] == '-h':" << endl
+ << indent_str() << "parts = sys.argv[argi + 1].split(':')" << endl
+ << indent_str() << "host = parts[0]" << endl
+ << indent_str() << "if len(parts) > 1:" << endl
+ << indent_str() << indent_str() << "port = int(parts[1])" << endl
+ << indent_str() << "argi += 2" << endl
+ << endl
+ << "if sys.argv[argi] == '-u':" << endl
+ << indent_str() << "url = urlparse(sys.argv[argi + 1])" << endl
+ << indent_str() << "parts = url[1].split(':')" << endl
+ << indent_str() << "host = parts[0]" << endl
+ << indent_str() << "if len(parts) > 1:" << endl
+ << indent_str() << indent_str() << "port = int(parts[1])" << endl
+ << indent_str() << "else:" << endl
+ << indent_str() << indent_str() << "port = 80" << endl
+ << indent_str() << "uri = url[2]" << endl
+ << indent_str() << "if url[4]:" << endl
+ << indent_str() << indent_str() << "uri += '?%s' % url[4]" << endl
+ << indent_str() << "http = True" << endl
+ << indent_str() << "argi += 2" << endl
+ << endl
+ << "if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed':" << endl
+ << indent_str() << "framed = True" << endl
+ << indent_str() << "argi += 1" << endl
+ << endl
+ << "if sys.argv[argi] == '-s' or sys.argv[argi] == '-ssl':" << endl
+ << indent_str() << "ssl = True" << endl
+ << indent_str() << "argi += 1" << endl
+ << endl
+ << "if sys.argv[argi] == '-novalidate':" << endl
+ << indent_str() << "validate = False" << endl
+ << indent_str() << "argi += 1" << endl
+ << endl
+ << "if sys.argv[argi] == '-ca_certs':" << endl
+ << indent_str() << "ca_certs = sys.argv[argi+1]" << endl
+ << indent_str() << "argi += 2" << endl
+ << endl
+ << "if sys.argv[argi] == '-keyfile':" << endl
+ << indent_str() << "keyfile = sys.argv[argi+1]" << endl
+ << indent_str() << "argi += 2" << endl
+ << endl
+ << "if sys.argv[argi] == '-certfile':" << endl
+ << indent_str() << "certfile = sys.argv[argi+1]" << endl
+ << indent_str() << "argi += 2" << endl
+ << endl
+ << "cmd = sys.argv[argi]" << endl
+ << "args = sys.argv[argi + 1:]" << endl
+ << endl
+ << "if http:" << endl
+ << indent_str() << "transport = THttpClient.THttpClient(host, port, uri)" << endl
+ << "else:" << endl
+ << indent_str() << "if ssl:" << endl
+ << indent_str() << indent_str() << "socket = TSSLSocket.TSSLSocket(host, port, "
+ "validate=validate, ca_certs=ca_certs, keyfile=keyfile, certfile=certfile)"
+ << endl
+ << indent_str() << "else:" << endl
+ << indent_str() << indent_str() << "socket = TSocket.TSocket(host, port)" << endl
+ << indent_str() << "if framed:" << endl
+ << indent_str() << indent_str() << "transport = TTransport.TFramedTransport(socket)" << endl
+ << indent_str() << "else:" << endl
+ << indent_str() << indent_str() << "transport = TTransport.TBufferedTransport(socket)" << endl
+ << "protocol = TBinaryProtocol(transport)" << endl
+ << "client = " << service_name_ << ".Client(protocol)" << endl
+ << "transport.open()" << endl
+ << endl;
+
+ // Generate the dispatch methods
+ bool first = true;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_remote << "el";
+ }
+
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const std::vector<t_field*>& args = arg_struct->get_members();
+ std::vector<t_field*>::size_type num_args = args.size();
+
+ f_remote << "if cmd == '" << (*f_iter)->get_name() << "':" << endl;
+ indent_up();
+ f_remote << indent() << "if len(args) != " << num_args << ":" << endl
+ << indent() << indent_str() << "print('" << (*f_iter)->get_name() << " requires " << num_args
+ << " args')" << endl
+ << indent() << indent_str() << "sys.exit(1)" << endl
+ << indent() << "pp.pprint(client." << (*f_iter)->get_name() << "(";
+ indent_down();
+ bool first_arg = true;
+ for (std::vector<t_field*>::size_type i = 0; i < num_args; ++i) {
+ if (first_arg)
+ first_arg = false;
+ else
+ f_remote << " ";
+ if (args[i]->get_type()->is_string()) {
+ f_remote << "args[" << i << "],";
+ } else {
+ f_remote << "eval(args[" << i << "]),";
+ }
+ }
+ f_remote << "))" << endl;
+
+ f_remote << endl;
+ }
+
+ if (functions.size() > 0) {
+ f_remote << "else:" << endl;
+ f_remote << indent_str() << "print('Unrecognized method %s' % cmd)" << endl;
+ f_remote << indent_str() << "sys.exit(1)" << endl;
+ f_remote << endl;
+ }
+
+ f_remote << "transport.close()" << endl;
+
+ // Close service file
+ f_remote.close();
+
+#ifndef _MSC_VER
+
+ // Make file executable, love that bitwise OR action
+ chmod(f_remote_name.c_str(),
+ S_IRUSR | S_IWUSR | S_IXUSR
+#ifndef _WIN32
+ | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+#endif
+ );
+
+#endif // _MSC_VER
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_py_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_processor = extends + ".Processor, ";
+ }
+
+ f_service_ << endl << endl;
+
+ // Generate the header portion
+ if (gen_zope_interface_) {
+ f_service_ << "@implementer(Iface)" << endl
+ << "class Processor(" << extends_processor << "TProcessor):" << endl;
+ } else {
+ f_service_ << "class Processor(" << extends_processor << "Iface, TProcessor):" << endl;
+ }
+
+ indent_up();
+
+ indent(f_service_) << "def __init__(self, handler):" << endl;
+ indent_up();
+ if (extends.empty()) {
+ if (gen_zope_interface_) {
+ f_service_ << indent() << "self._handler = Iface(handler)" << endl;
+ } else {
+ f_service_ << indent() << "self._handler = handler" << endl;
+ }
+
+ f_service_ << indent() << "self._processMap = {}" << endl;
+ } else {
+ if (gen_zope_interface_) {
+ f_service_ << indent() << extends << ".Processor.__init__(self, Iface(handler))" << endl;
+ } else {
+ f_service_ << indent() << extends << ".Processor.__init__(self, handler)" << endl;
+ }
+ }
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << indent() << "self._processMap[\"" << (*f_iter)->get_name()
+ << "\"] = Processor.process_" << (*f_iter)->get_name() << endl;
+ }
+ f_service_ << indent() << "self._on_message_begin = None" << endl;
+ indent_down();
+ f_service_ << endl;
+
+ f_service_ << indent() << "def on_message_begin(self, func):" << endl;
+ indent_up();
+ f_service_ << indent() << "self._on_message_begin = func" << endl;
+ indent_down();
+ f_service_ << endl;
+
+ // Generate the server implementation
+ f_service_ << indent() << "def process(self, iprot, oprot):" << endl;
+ indent_up();
+
+ f_service_ << indent() << "(name, type, seqid) = iprot.readMessageBegin()" << endl;
+ f_service_ << indent() << "if self._on_message_begin:" << endl;
+ indent_up();
+ f_service_ << indent() << "self._on_message_begin(name, type, seqid)" << endl;
+ indent_down();
+
+ // TODO(mcslee): validate message
+
+ // HOT: dictionary function lookup
+ f_service_ << indent() << "if name not in self._processMap:" << endl;
+ indent_up();
+ f_service_ << indent() << "iprot.skip(TType.STRUCT)" << endl
+ << indent() << "iprot.readMessageEnd()" << endl
+ << indent()
+ << "x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown "
+ "function %s' % (name))"
+ << endl
+ << indent() << "oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid)" << endl
+ << indent() << "x.write(oprot)" << endl
+ << indent() << "oprot.writeMessageEnd()" << endl
+ << indent() << "oprot.trans.flush()" << endl;
+
+ if (gen_twisted_) {
+ f_service_ << indent() << "return defer.succeed(None)" << endl;
+ } else {
+ f_service_ << indent() << "return" << endl;
+ }
+ indent_down();
+
+ f_service_ << indent() << "else:" << endl;
+
+ if (gen_twisted_ || gen_tornado_) {
+ f_service_ << indent() << indent_str()
+ << "return self._processMap[name](self, seqid, iprot, oprot)" << endl;
+ } else {
+ f_service_ << indent() << indent_str() << "self._processMap[name](self, seqid, iprot, oprot)"
+ << endl;
+
+ // Read end of args field, the T_STOP, and the struct close
+ f_service_ << indent() << "return True" << endl;
+ }
+
+ indent_down();
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ f_service_ << endl;
+ generate_process_function(tservice, *f_iter);
+ }
+
+ indent_down();
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_py_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open function
+ if (gen_tornado_) {
+ f_service_ << indent() << "@gen.coroutine" << endl << indent() << "def process_"
+ << tfunction->get_name() << "(self, seqid, iprot, oprot):" << endl;
+ } else {
+ f_service_ << indent() << "def process_" << tfunction->get_name()
+ << "(self, seqid, iprot, oprot):" << endl;
+ }
+
+ indent_up();
+
+ string argsname = tfunction->get_name() + "_args";
+ string resultname = tfunction->get_name() + "_result";
+
+ f_service_ << indent() << "args = " << argsname << "()" << endl << indent() << "args.read(iprot)"
+ << endl << indent() << "iprot.readMessageEnd()" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "result = " << resultname << "()" << endl;
+ }
+
+ if (gen_twisted_) {
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent() << "d = defer.maybeDeferred(self._handler." << tfunction->get_name()
+ << ", ";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ")" << endl;
+
+ if (tfunction->is_oneway()) {
+ f_service_ << indent() << "d.addErrback(self.handle_exception_" << tfunction->get_name()
+ << ", seqid)" << endl;
+ } else {
+ f_service_ << indent() << "d.addCallback(self.write_results_success_" << tfunction->get_name()
+ << ", result, seqid, oprot)" << endl
+ << indent() << "d.addErrback(self.write_results_exception_"
+ << tfunction->get_name() << ", result, seqid, oprot)" << endl;
+ }
+ f_service_ << indent() << "return d" << endl << endl;
+
+ indent_down();
+
+ if (tfunction->is_oneway()) {
+ indent(f_service_) << "def handle_exception_" << tfunction->get_name()
+ << "(self, error, seqid):" << endl;
+ } else {
+ indent(f_service_) << "def write_results_success_" << tfunction->get_name()
+ << "(self, success, result, seqid, oprot):" << endl;
+ indent_up();
+ if (!tfunction->get_returntype()->is_void()) {
+ f_service_ << indent() << "result.success = success" << endl;
+ }
+ f_service_ << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", TMessageType.REPLY, seqid)" << endl
+ << indent() << "result.write(oprot)" << endl
+ << indent() << "oprot.writeMessageEnd()" << endl
+ << indent() << "oprot.trans.flush()" << endl
+ << endl;
+ indent_down();
+
+ indent(f_service_) << "def write_results_exception_" << tfunction->get_name()
+ << "(self, error, result, seqid, oprot):" << endl;
+ }
+ indent_up();
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl;
+ }
+ f_service_ << indent() << "try:" << endl;
+
+ // Kinda absurd
+ f_service_ << indent() << indent_str() << "error.raiseException()" << endl;
+ if (!tfunction->is_oneway()) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ const string& xname = (*x_iter)->get_name();
+ f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as " << xname
+ << ":" << endl;
+ indent_up();
+ f_service_ << indent() << "result." << xname << " = " << xname << endl;
+ indent_down();
+ }
+ }
+ f_service_ << indent() << "except TTransport.TTransportException:" << endl
+ << indent() << indent_str() << "raise" << endl;
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "except TApplicationException as ex:" << endl
+ << indent() << indent_str()
+ << "logging.exception('TApplication exception in handler')" << endl
+ << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl
+ << indent() << indent_str() << "result = ex" << endl
+ << indent() << "except Exception:" << endl
+ << indent() << indent_str()
+ << "logging.exception('Unexpected exception in handler')" << endl
+ << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl
+ << indent() << indent_str()
+ << "result = TApplicationException(TApplicationException.INTERNAL_ERROR, "
+ "'Internal error')"
+ << endl
+ << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", msg_type, seqid)" << endl
+ << indent() << "result.write(oprot)" << endl
+ << indent() << "oprot.writeMessageEnd()" << endl
+ << indent() << "oprot.trans.flush()" << endl;
+ } else {
+ f_service_ << indent() << "except Exception:" << endl
+ << indent() << indent_str()
+ << "logging.exception('Exception in oneway handler')" << endl;
+ }
+ indent_down();
+
+ } else if (gen_tornado_) {
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (!tfunction->is_oneway()) {
+ indent(f_service_) << "msg_type = TMessageType.REPLY" << endl;
+ }
+ f_service_ << indent() << "try:" << endl;
+ indent_up();
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.success = ";
+ }
+ f_service_ << "yield gen.maybe_future(self._handler." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << "))" << endl;
+
+ indent_down();
+ if (!tfunction->is_oneway()) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ const string& xname = (*x_iter)->get_name();
+ f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as " << xname
+ << ":" << endl
+ << indent() << indent_str() << "result." << xname << " = " << xname << endl;
+ }
+ }
+ f_service_ << indent() << "except TTransport.TTransportException:" << endl
+ << indent() << indent_str() << "raise" << endl;
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "except TApplicationException as ex:" << endl
+ << indent() << indent_str()
+ << "logging.exception('TApplication exception in handler')" << endl
+ << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl
+ << indent() << indent_str() << "result = ex" << endl
+ << indent() << "except Exception:" << endl
+ << indent() << indent_str()
+ << "logging.exception('Unexpected exception in handler')" << endl
+ << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl
+ << indent() << indent_str()
+ << "result = TApplicationException(TApplicationException.INTERNAL_ERROR, "
+ "'Internal error')"
+ << endl;
+ } else {
+ f_service_ << indent() << "except Exception:" << endl
+ << indent() << indent_str()
+ << "logging.exception('Exception in oneway handler')" << endl;
+ }
+
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", msg_type, seqid)" << endl
+ << indent() << "result.write(oprot)" << endl
+ << indent() << "oprot.writeMessageEnd()" << endl
+ << indent() << "oprot.trans.flush()" << endl;
+ }
+
+ // Close function
+ indent_down();
+
+ } else { // py
+ // Try block for a function with exceptions
+ // It also catches arbitrary exceptions raised by handler method to propagate them to the client
+ f_service_ << indent() << "try:" << endl;
+ indent_up();
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ << indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.success = ";
+ }
+ f_service_ << "self._handler." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ")" << endl;
+ if (!tfunction->is_oneway()) {
+ f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl;
+ }
+
+ indent_down();
+ f_service_ << indent()
+ << "except TTransport.TTransportException:" << endl
+ << indent() << indent_str() << "raise" << endl;
+
+ if (!tfunction->is_oneway()) {
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ const string& xname = (*x_iter)->get_name();
+ f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as " << xname
+ << ":" << endl;
+ indent_up();
+ f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl;
+ f_service_ << indent() << "result." << xname << " = " << xname << endl;
+ indent_down();
+ }
+
+ f_service_ << indent() << "except TApplicationException as ex:" << endl
+ << indent() << indent_str()
+ << "logging.exception('TApplication exception in handler')" << endl
+ << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl
+ << indent() << indent_str() << "result = ex" << endl
+ << indent() << "except Exception:" << endl
+ << indent() << indent_str()
+ << "logging.exception('Unexpected exception in handler')" << endl
+ << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl
+ << indent() << indent_str()
+ << "result = TApplicationException(TApplicationException.INTERNAL_ERROR, "
+ "'Internal error')"
+ << endl
+ << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name()
+ << "\", msg_type, seqid)" << endl
+ << indent() << "result.write(oprot)" << endl
+ << indent() << "oprot.writeMessageEnd()" << endl
+ << indent() << "oprot.trans.flush()" << endl;
+ } else {
+ f_service_ << indent() << "except Exception:" << endl
+ << indent() << indent_str() << "logging.exception('Exception in oneway handler')" << endl;
+ }
+
+ // Close function
+ indent_down();
+ }
+}
+
+/**
+ * Deserializes a field of any type.
+ */
+void t_py_generator::generate_deserialize_field(ostream& out,
+ t_field* tfield,
+ string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ if (type->is_void()) {
+ throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ string name = prefix + tfield->get_name();
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_deserialize_struct(out, (t_struct*)type, name);
+ } else if (type->is_container()) {
+ generate_deserialize_container(out, type, name);
+ } else if (type->is_base_type() || type->is_enum()) {
+ indent(out) << name << " = iprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "readBinary()";
+ } else if(!gen_utf8strings_) {
+ out << "readString()";
+ } else {
+ out << "readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "readBool()";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "readByte()";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "readI16()";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "readI32()";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "readI64()";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "readDouble()";
+ break;
+ default:
+ throw "compiler error: no Python name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "readI32()";
+ }
+ out << endl;
+
+ } else {
+ printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Generates an unserializer for a struct, calling read()
+ */
+void t_py_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ if (is_immutable(tstruct)) {
+ out << indent() << prefix << " = " << type_name(tstruct) << ".read(iprot)" << endl;
+ } else {
+ out << indent() << prefix << " = " << type_name(tstruct) << "()" << endl
+ << indent() << prefix << ".read(iprot)" << endl;
+ }
+}
+
+/**
+ * Serialize a container by writing out the header followed by
+ * data and then a footer.
+ */
+void t_py_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) {
+ string size = tmp("_size");
+ string ktype = tmp("_ktype");
+ string vtype = tmp("_vtype");
+ string etype = tmp("_etype");
+
+ t_field fsize(g_type_i32, size);
+ t_field fktype(g_type_i8, ktype);
+ t_field fvtype(g_type_i8, vtype);
+ t_field fetype(g_type_i8, etype);
+
+ // Declare variables, read header
+ if (ttype->is_map()) {
+ out << indent() << prefix << " = {}" << endl << indent() << "(" << ktype << ", " << vtype
+ << ", " << size << ") = iprot.readMapBegin()" << endl;
+ } else if (ttype->is_set()) {
+ out << indent() << prefix << " = set()" << endl << indent() << "(" << etype << ", " << size
+ << ") = iprot.readSetBegin()" << endl;
+ } else if (ttype->is_list()) {
+ out << indent() << prefix << " = []" << endl << indent() << "(" << etype << ", " << size
+ << ") = iprot.readListBegin()" << endl;
+ }
+
+ // For loop iterates over elements
+ string i = tmp("_i");
+ indent(out) <<
+ "for " << i << " in range(" << size << "):" << endl;
+
+ indent_up();
+
+ if (ttype->is_map()) {
+ generate_deserialize_map_element(out, (t_map*)ttype, prefix);
+ } else if (ttype->is_set()) {
+ generate_deserialize_set_element(out, (t_set*)ttype, prefix);
+ } else if (ttype->is_list()) {
+ generate_deserialize_list_element(out, (t_list*)ttype, prefix);
+ }
+
+ indent_down();
+
+ // Read container end
+ if (ttype->is_map()) {
+ indent(out) << "iprot.readMapEnd()" << endl;
+ if (is_immutable(ttype)) {
+ indent(out) << prefix << " = TFrozenDict(" << prefix << ")" << endl;
+ }
+ } else if (ttype->is_set()) {
+ indent(out) << "iprot.readSetEnd()" << endl;
+ if (is_immutable(ttype)) {
+ indent(out) << prefix << " = frozenset(" << prefix << ")" << endl;
+ }
+ } else if (ttype->is_list()) {
+ if (is_immutable(ttype)) {
+ indent(out) << prefix << " = tuple(" << prefix << ")" << endl;
+ }
+ indent(out) << "iprot.readListEnd()" << endl;
+ }
+}
+
+/**
+ * Generates code to deserialize a map
+ */
+void t_py_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) {
+ string key = tmp("_key");
+ string val = tmp("_val");
+ t_field fkey(tmap->get_key_type(), key);
+ t_field fval(tmap->get_val_type(), val);
+
+ generate_deserialize_field(out, &fkey);
+ generate_deserialize_field(out, &fval);
+
+ indent(out) << prefix << "[" << key << "] = " << val << endl;
+}
+
+/**
+ * Write a set element
+ */
+void t_py_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tset->get_elem_type(), elem);
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".add(" << elem << ")" << endl;
+}
+
+/**
+ * Write a list element
+ */
+void t_py_generator::generate_deserialize_list_element(ostream& out,
+ t_list* tlist,
+ string prefix) {
+ string elem = tmp("_elem");
+ t_field felem(tlist->get_elem_type(), elem);
+
+ generate_deserialize_field(out, &felem);
+
+ indent(out) << prefix << ".append(" << elem << ")" << endl;
+}
+
+/**
+ * Serializes a field of any type.
+ *
+ * @param tfield The field to serialize
+ * @param prefix Name to prepend to field name
+ */
+void t_py_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
+ t_type* type = get_true_type(tfield->get_type());
+
+ // Do nothing for void types
+ if (type->is_void()) {
+ throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
+ }
+
+ if (type->is_struct() || type->is_xception()) {
+ generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name());
+ } else if (type->is_container()) {
+ generate_serialize_container(out, type, prefix + tfield->get_name());
+ } else if (type->is_base_type() || type->is_enum()) {
+
+ string name = prefix + tfield->get_name();
+
+ indent(out) << "oprot.";
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "compiler error: cannot serialize void field in a struct: " + name;
+ break;
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ out << "writeBinary(" << name << ")";
+ } else if (!gen_utf8strings_) {
+ out << "writeString(" << name << ")";
+ } else {
+ out << "writeString(" << name << ".encode('utf-8') if sys.version_info[0] == 2 else " << name << ")";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << "writeBool(" << name << ")";
+ break;
+ case t_base_type::TYPE_I8:
+ out << "writeByte(" << name << ")";
+ break;
+ case t_base_type::TYPE_I16:
+ out << "writeI16(" << name << ")";
+ break;
+ case t_base_type::TYPE_I32:
+ out << "writeI32(" << name << ")";
+ break;
+ case t_base_type::TYPE_I64:
+ out << "writeI64(" << name << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << "writeDouble(" << name << ")";
+ break;
+ default:
+ throw "compiler error: no Python name for base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << "writeI32(" << name << ")";
+ }
+ out << endl;
+ } else {
+ printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n",
+ prefix.c_str(),
+ tfield->get_name().c_str(),
+ type->get_name().c_str());
+ }
+}
+
+/**
+ * Serializes all the members of a struct.
+ *
+ * @param tstruct The struct to serialize
+ * @param prefix String prefix to attach to all fields
+ */
+void t_py_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
+ (void)tstruct;
+ indent(out) << prefix << ".write(oprot)" << endl;
+}
+
+void t_py_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) {
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
+ << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
+ << "len(" << prefix << "))" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
+ << "len(" << prefix << "))" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type())
+ << ", "
+ << "len(" << prefix << "))" << endl;
+ }
+
+ if (ttype->is_map()) {
+ string kiter = tmp("kiter");
+ string viter = tmp("viter");
+ indent(out) << "for " << kiter << ", " << viter << " in " << prefix << ".items():" << endl;
+ indent_up();
+ generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
+ indent_down();
+ } else if (ttype->is_set()) {
+ string iter = tmp("iter");
+ indent(out) << "for " << iter << " in " << prefix << ":" << endl;
+ indent_up();
+ generate_serialize_set_element(out, (t_set*)ttype, iter);
+ indent_down();
+ } else if (ttype->is_list()) {
+ string iter = tmp("iter");
+ indent(out) << "for " << iter << " in " << prefix << ":" << endl;
+ indent_up();
+ generate_serialize_list_element(out, (t_list*)ttype, iter);
+ indent_down();
+ }
+
+ if (ttype->is_map()) {
+ indent(out) << "oprot.writeMapEnd()" << endl;
+ } else if (ttype->is_set()) {
+ indent(out) << "oprot.writeSetEnd()" << endl;
+ } else if (ttype->is_list()) {
+ indent(out) << "oprot.writeListEnd()" << endl;
+ }
+}
+
+/**
+ * Serializes the members of a map.
+ *
+ */
+void t_py_generator::generate_serialize_map_element(ostream& out,
+ t_map* tmap,
+ string kiter,
+ string viter) {
+ t_field kfield(tmap->get_key_type(), kiter);
+ generate_serialize_field(out, &kfield, "");
+
+ t_field vfield(tmap->get_val_type(), viter);
+ generate_serialize_field(out, &vfield, "");
+}
+
+/**
+ * Serializes the members of a set.
+ */
+void t_py_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) {
+ t_field efield(tset->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Serializes the members of a list.
+ */
+void t_py_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) {
+ t_field efield(tlist->get_elem_type(), iter);
+ generate_serialize_field(out, &efield, "");
+}
+
+/**
+ * Generates the docstring for a given struct.
+ */
+void t_py_generator::generate_python_docstring(ostream& out, t_struct* tstruct) {
+ generate_python_docstring(out, tstruct, tstruct, "Attributes");
+}
+
+/**
+ * Generates the docstring for a given function.
+ */
+void t_py_generator::generate_python_docstring(ostream& out, t_function* tfunction) {
+ generate_python_docstring(out, tfunction, tfunction->get_arglist(), "Parameters");
+}
+
+/**
+ * Generates the docstring for a struct or function.
+ */
+void t_py_generator::generate_python_docstring(ostream& out,
+ t_doc* tdoc,
+ t_struct* tstruct,
+ const char* subheader) {
+ bool has_doc = false;
+ stringstream ss;
+ if (tdoc->has_doc()) {
+ has_doc = true;
+ ss << tdoc->get_doc();
+ }
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ if (fields.size() > 0) {
+ if (has_doc) {
+ ss << endl;
+ }
+ has_doc = true;
+ ss << subheader << ":\n";
+ vector<t_field*>::const_iterator p_iter;
+ for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
+ t_field* p = *p_iter;
+ ss << " - " << p->get_name();
+ if (p->has_doc()) {
+ ss << ": " << p->get_doc();
+ } else {
+ ss << endl;
+ }
+ }
+ }
+
+ if (has_doc) {
+ generate_docstring_comment(out, "\"\"\"\n", "", ss.str(), "\"\"\"\n");
+ }
+}
+
+/**
+ * Generates the docstring for a generic object.
+ */
+void t_py_generator::generate_python_docstring(ostream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ generate_docstring_comment(out, "\"\"\"\n", "", tdoc->get_doc(), "\"\"\"\n");
+ }
+}
+
+/**
+ * Declares an argument, which may include initialization as necessary.
+ *
+ * @param tfield The field
+ */
+string t_py_generator::declare_argument(t_field* tfield) {
+ std::ostringstream result;
+ result << tfield->get_name() << "=";
+ if (tfield->get_value() != NULL) {
+ result << render_field_default_value(tfield);
+ } else {
+ result << "None";
+ }
+ return result.str();
+}
+
+/**
+ * Renders a field default value, returns None otherwise.
+ *
+ * @param tfield The field
+ */
+string t_py_generator::render_field_default_value(t_field* tfield) {
+ t_type* type = get_true_type(tfield->get_type());
+ if (tfield->get_value() != NULL) {
+ return render_const_value(type, tfield->get_value());
+ } else {
+ return "None";
+ }
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_py_generator::function_signature(t_function* tfunction, bool interface) {
+ vector<string> pre;
+ vector<string> post;
+ string signature = tfunction->get_name() + "(";
+
+ if (!(gen_zope_interface_ && interface)) {
+ pre.emplace_back("self");
+ }
+
+ signature += argument_list(tfunction->get_arglist(), &pre, &post) + ")";
+ return signature;
+}
+
+/**
+ * Renders a field list
+ */
+string t_py_generator::argument_list(t_struct* tstruct, vector<string>* pre, vector<string>* post) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ vector<string>::const_iterator s_iter;
+ bool first = true;
+ if (pre) {
+ for (s_iter = pre->begin(); s_iter != pre->end(); ++s_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += *s_iter;
+ }
+ }
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += (*f_iter)->get_name();
+ }
+ if (post) {
+ for (s_iter = post->begin(); s_iter != post->end(); ++s_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += *s_iter;
+ }
+ }
+ return result;
+}
+
+string t_py_generator::type_name(t_type* ttype) {
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ t_program* program = ttype->get_program();
+ if (ttype->is_service()) {
+ return get_real_py_module(program, gen_twisted_, package_prefix_) + "." + ttype->get_name();
+ }
+ if (program != NULL && program != program_) {
+ return get_real_py_module(program, gen_twisted_, package_prefix_) + ".ttypes." + ttype->get_name();
+ }
+ return ttype->get_name();
+}
+
+/**
+ * Converts the parse type to a Python tyoe
+ */
+string t_py_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType.STRING";
+ case t_base_type::TYPE_BOOL:
+ return "TType.BOOL";
+ case t_base_type::TYPE_I8:
+ return "TType.BYTE";
+ case t_base_type::TYPE_I16:
+ return "TType.I16";
+ case t_base_type::TYPE_I32:
+ return "TType.I32";
+ case t_base_type::TYPE_I64:
+ return "TType.I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType.DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "TType.I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType.STRUCT";
+ } else if (type->is_map()) {
+ return "TType.MAP";
+ } else if (type->is_set()) {
+ return "TType.SET";
+ } else if (type->is_list()) {
+ return "TType.LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+/** See the comment inside generate_py_struct_definition for what this is. */
+string t_py_generator::type_to_spec_args(t_type* ttype) {
+ while (ttype->is_typedef()) {
+ ttype = ((t_typedef*)ttype)->get_type();
+ }
+
+ if (ttype->is_binary()) {
+ return "'BINARY'";
+ } else if (gen_utf8strings_ && ttype->is_base_type()
+ && reinterpret_cast<t_base_type*>(ttype)->is_string()) {
+ return "'UTF8'";
+ } else if (ttype->is_base_type() || ttype->is_enum()) {
+ return "None";
+ } else if (ttype->is_struct() || ttype->is_xception()) {
+ return "[" + type_name(ttype) + ", None]";
+ } else if (ttype->is_map()) {
+ return "(" + type_to_enum(((t_map*)ttype)->get_key_type()) + ", "
+ + type_to_spec_args(((t_map*)ttype)->get_key_type()) + ", "
+ + type_to_enum(((t_map*)ttype)->get_val_type()) + ", "
+ + type_to_spec_args(((t_map*)ttype)->get_val_type()) + ", "
+ + (is_immutable(ttype) ? "True" : "False") + ")";
+
+ } else if (ttype->is_set()) {
+ return "(" + type_to_enum(((t_set*)ttype)->get_elem_type()) + ", "
+ + type_to_spec_args(((t_set*)ttype)->get_elem_type()) + ", "
+ + (is_immutable(ttype) ? "True" : "False") + ")";
+
+ } else if (ttype->is_list()) {
+ return "(" + type_to_enum(((t_list*)ttype)->get_elem_type()) + ", "
+ + type_to_spec_args(((t_list*)ttype)->get_elem_type()) + ", "
+ + (is_immutable(ttype) ? "True" : "False") + ")";
+ }
+
+ throw "INVALID TYPE IN type_to_spec_args: " + ttype->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(
+ py,
+ "Python",
+ " zope.interface: Generate code for use with zope.interface.\n"
+ " twisted: Generate Twisted-friendly RPC services.\n"
+ " tornado: Generate code for use with Tornado.\n"
+ " no_utf8strings: Do not Encode/decode strings using utf8 in the generated code. Basically no effect for Python 3.\n"
+ " coding=CODING: Add file encoding declare in generated file.\n"
+ " slots: Generate code using slots for instance members.\n"
+ " dynamic: Generate dynamic code, less code generated but slower.\n"
+ " dynbase=CLS Derive generated classes from class CLS instead of TBase.\n"
+ " dynfrozen=CLS Derive generated immutable classes from class CLS instead of TFrozenBase.\n"
+ " dynexc=CLS Derive generated exceptions from CLS instead of TExceptionBase.\n"
+ " dynimport='from foo.bar import CLS'\n"
+ " Add an import line to generated code to find the dynbase class.\n"
+ " package_prefix='top.package.'\n"
+ " Package prefix for generated files.\n"
+ " old_style: Deprecated. Generate old-style classes.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc
new file mode 100644
index 000000000..61c34811d
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc
@@ -0,0 +1,1288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <algorithm>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * A subclass of std::ofstream that includes indenting functionality.
+ */
+class t_rb_ofstream : public std::ofstream {
+private:
+ int indent_;
+
+public:
+ t_rb_ofstream() : std::ofstream(), indent_(0) {}
+ explicit t_rb_ofstream(const char* filename,
+ ios_base::openmode mode = ios_base::out,
+ int indent = 0)
+ : std::ofstream(filename, mode), indent_(indent) {}
+
+ t_rb_ofstream& indent() {
+ for (int i = 0; i < indent_; ++i) {
+ *this << " ";
+ }
+ return *this;
+ }
+
+ void indent_up() { indent_++; }
+ void indent_down() { indent_--; }
+};
+
+/**
+ * Ruby code generator.
+ *
+ */
+class t_rb_generator : public t_oop_generator {
+public:
+ t_rb_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ require_rubygems_ = false;
+ namespaced_ = false;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("rubygems") == 0) {
+ require_rubygems_ = true;
+ } else if( iter->first.compare("namespaced") == 0) {
+ namespaced_ = true;
+ } else {
+ throw "unknown option ruby:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-rb";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_forward_declaration(t_struct* tstruct) override;
+ void generate_union(t_struct* tunion);
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+ t_rb_ofstream& render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_rb_struct_declaration(t_rb_ofstream& out, t_struct* tstruct, bool is_exception);
+ void generate_rb_struct(t_rb_ofstream& out, t_struct* tstruct, bool is_exception);
+ void generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_rb_union(t_rb_ofstream& out, t_struct* tstruct, bool is_exception);
+ void generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_rb_function_helpers(t_function* tfunction);
+ void generate_rb_simple_constructor(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_rb_simple_exception_constructor(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_field_constants(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_field_constructors(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_field_defns(t_rb_ofstream& out, t_struct* tstruct);
+ void generate_field_data(t_rb_ofstream& out,
+ t_type* field_type,
+ const std::string& field_name,
+ t_const_value* field_value,
+ bool optional);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_helpers(t_service* tservice);
+ void generate_service_interface(t_service* tservice);
+ void generate_service_client(t_service* tservice);
+ void generate_service_server(t_service* tservice);
+ void generate_process_function(t_service* tservice, t_function* tfunction);
+
+ /**
+ * Serialization constructs
+ */
+
+ void generate_deserialize_field(t_rb_ofstream& out,
+ t_field* tfield,
+ std::string prefix = "",
+ bool inclass = false);
+
+ void generate_deserialize_struct(t_rb_ofstream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_deserialize_container(t_rb_ofstream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_deserialize_set_element(t_rb_ofstream& out, t_set* tset, std::string prefix = "");
+
+ void generate_deserialize_map_element(t_rb_ofstream& out, t_map* tmap, std::string prefix = "");
+
+ void generate_deserialize_list_element(t_rb_ofstream& out,
+ t_list* tlist,
+ std::string prefix = "");
+
+ void generate_serialize_field(t_rb_ofstream& out, t_field* tfield, std::string prefix = "");
+
+ void generate_serialize_struct(t_rb_ofstream& out, t_struct* tstruct, std::string prefix = "");
+
+ void generate_serialize_container(t_rb_ofstream& out, t_type* ttype, std::string prefix = "");
+
+ void generate_serialize_map_element(t_rb_ofstream& out,
+ t_map* tmap,
+ std::string kiter,
+ std::string viter);
+
+ void generate_serialize_set_element(t_rb_ofstream& out, t_set* tmap, std::string iter);
+
+ void generate_serialize_list_element(t_rb_ofstream& out, t_list* tlist, std::string iter);
+
+ void generate_rdoc(t_rb_ofstream& out, t_doc* tdoc);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string rb_autogen_comment();
+ std::string render_require_thrift();
+ std::string render_includes();
+ std::string declare_field(t_field* tfield);
+ std::string type_name(const t_type* ttype);
+ std::string full_type_name(const t_type* ttype);
+ std::string function_signature(t_function* tfunction, std::string prefix = "");
+ std::string argument_list(t_struct* tstruct);
+ std::string type_to_enum(t_type* ttype);
+ std::string rb_namespace_to_path_prefix(std::string rb_namespace);
+
+ std::vector<std::string> ruby_modules(const t_program* p) {
+ std::string ns = p->get_namespace("rb");
+ std::vector<std::string> modules;
+ if (ns.empty()) {
+ return modules;
+ }
+
+ std::string::iterator pos = ns.begin();
+ while (true) {
+ std::string::iterator delim = std::find(pos, ns.end(), '.');
+ modules.push_back(capitalize(std::string(pos, delim)));
+ pos = delim;
+ if (pos == ns.end()) {
+ break;
+ }
+ ++pos;
+ }
+
+ return modules;
+ }
+
+ void begin_namespace(t_rb_ofstream&, std::vector<std::string>);
+ void end_namespace(t_rb_ofstream&, std::vector<std::string>);
+
+private:
+ /**
+ * File streams
+ */
+
+ t_rb_ofstream f_types_;
+ t_rb_ofstream f_consts_;
+ t_rb_ofstream f_service_;
+
+ std::string namespace_dir_;
+ std::string require_prefix_;
+
+ /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */
+ bool require_rubygems_;
+
+ /** If true, generate files in idiomatic namespaced directories. */
+ bool namespaced_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_rb_generator::init_generator() {
+ string subdir = get_out_dir();
+
+ // Make output directory
+ MKDIR(subdir.c_str());
+
+ if (namespaced_) {
+ require_prefix_ = rb_namespace_to_path_prefix(program_->get_namespace("rb"));
+
+ string dir = require_prefix_;
+ string::size_type loc;
+
+ while ((loc = dir.find("/")) != string::npos) {
+ subdir = subdir + dir.substr(0, loc) + "/";
+ MKDIR(subdir.c_str());
+ dir = dir.substr(loc + 1);
+ }
+ }
+
+ namespace_dir_ = subdir;
+
+ // Make output file
+ string f_types_name = namespace_dir_ + underscore(program_name_) + "_types.rb";
+ f_types_.open(f_types_name.c_str());
+
+ string f_consts_name = namespace_dir_ + underscore(program_name_) + "_constants.rb";
+ f_consts_.open(f_consts_name.c_str());
+
+ // Print header
+ f_types_ << rb_autogen_comment() << endl << render_require_thrift() << render_includes() << endl;
+ begin_namespace(f_types_, ruby_modules(program_));
+
+ f_consts_ << rb_autogen_comment() << endl << render_require_thrift() << "require '"
+ << require_prefix_ << underscore(program_name_) << "_types'" << endl << endl;
+ begin_namespace(f_consts_, ruby_modules(program_));
+}
+
+/**
+ * Renders the require of thrift itself, and possibly of the rubygems dependency.
+ */
+string t_rb_generator::render_require_thrift() {
+ if (require_rubygems_) {
+ return "require 'rubygems'\nrequire 'thrift'\n";
+ } else {
+ return "require 'thrift'\n";
+ }
+}
+
+/**
+ * Renders all the imports necessary for including another Thrift program
+ */
+string t_rb_generator::render_includes() {
+ const vector<t_program*>& includes = program_->get_includes();
+ string result = "";
+ for (auto include : includes) {
+ if (namespaced_) {
+ t_program* included = include;
+ std::string included_require_prefix
+ = rb_namespace_to_path_prefix(included->get_namespace("rb"));
+ std::string included_name = included->get_name();
+ result += "require '" + included_require_prefix + underscore(included_name) + "_types'\n";
+ } else {
+ result += "require '" + underscore(include->get_name()) + "_types'\n";
+ }
+ }
+ if (includes.size() > 0) {
+ result += "\n";
+ }
+ return result;
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_rb_generator::rb_autogen_comment() {
+ return std::string("#\n") + "# Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + "#\n" + "# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "#\n";
+}
+
+/**
+ * Closes the type files
+ */
+void t_rb_generator::close_generator() {
+ // Close types file
+ end_namespace(f_types_, ruby_modules(program_));
+ end_namespace(f_consts_, ruby_modules(program_));
+ f_types_.close();
+ f_consts_.close();
+}
+
+/**
+ * Generates a typedef. This is not done in Ruby, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_rb_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+/**
+ * Generates code for an enumerated type. Done using a class to scope
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_rb_generator::generate_enum(t_enum* tenum) {
+ f_types_.indent() << "module " << capitalize(tenum->get_name()) << endl;
+ f_types_.indent_up();
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+
+ // Ruby class constants have to be capitalized... omg i am so on the fence
+ // about languages strictly enforcing capitalization why can't we just all
+ // agree and play nice.
+ string name = capitalize((*c_iter)->get_name());
+
+ generate_rdoc(f_types_, *c_iter);
+ f_types_.indent() << name << " = " << value << endl;
+ }
+
+ // Create a hash mapping values back to their names (as strings) since ruby has no native enum
+ // type
+ f_types_.indent() << "VALUE_MAP = {";
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ // Populate the hash
+ int value = (*c_iter)->get_value();
+ if (c_iter != constants.begin())
+ f_types_ << ", ";
+ f_types_ << value << " => \"" << capitalize((*c_iter)->get_name()) << "\"";
+ }
+ f_types_ << "}" << endl;
+
+ // Create a set with valid values for this enum
+ f_types_.indent() << "VALID_VALUES = Set.new([";
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ // Populate the set
+ if (c_iter != constants.begin())
+ f_types_ << ", ";
+ f_types_ << capitalize((*c_iter)->get_name());
+ }
+ f_types_ << "]).freeze" << endl;
+
+ f_types_.indent_down();
+ f_types_.indent() << "end" << endl << endl;
+}
+
+/**
+ * Generate a constant value
+ */
+void t_rb_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ name[0] = toupper(name[0]);
+
+ f_consts_.indent() << name << " = ";
+ render_const_value(f_consts_, type, value) << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out,
+ t_type* type,
+ t_const_value* value) {
+ type = get_true_type(type);
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << "%q\"" << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out.indent() << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << full_type_name(type) << ".new({" << endl;
+ out.indent_up();
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+ out.indent();
+ render_const_value(out, g_type_string, v_iter->first) << " => ";
+ render_const_value(out, field_type, v_iter->second) << "," << endl;
+ }
+ out.indent_down();
+ out.indent() << "})";
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "{" << endl;
+ out.indent_up();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out.indent();
+ render_const_value(out, ktype, v_iter->first) << " => ";
+ render_const_value(out, vtype, v_iter->second) << "," << endl;
+ }
+ out.indent_down();
+ out.indent() << "}";
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ if (type->is_set()) {
+ out << "Set.new([" << endl;
+ } else {
+ out << "[" << endl;
+ }
+ out.indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out.indent();
+ render_const_value(out, etype, *v_iter) << "," << endl;
+ }
+ out.indent_down();
+ if (type->is_set()) {
+ out.indent() << "])";
+ } else {
+ out.indent() << "]";
+ }
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+ return out;
+}
+
+/**
+ * Generates a ruby struct
+ */
+void t_rb_generator::generate_struct(t_struct* tstruct) {
+ if (tstruct->is_union()) {
+ generate_rb_union(f_types_, tstruct, false);
+ } else {
+ generate_rb_struct(f_types_, tstruct, false);
+ }
+}
+
+
+/**
+ * Generates the "forward declarations" for ruby structs.
+ * These are simply a declaration of each class with proper inheritance.
+ * The rest of the struct is still generated in generate_struct as has
+ * always been the case. These declarations allow thrift to generate valid
+ * ruby in cases where thrift structs rely on recursive definitions.
+ */
+void t_rb_generator::generate_forward_declaration(t_struct* tstruct) {
+ generate_rb_struct_declaration(f_types_, tstruct, tstruct->is_xception());
+}
+
+void t_rb_generator::generate_rb_struct_declaration(t_rb_ofstream& out, t_struct* tstruct, bool is_exception) {
+ out.indent() << "class " << type_name(tstruct);
+ if (tstruct->is_union()) {
+ out << " < ::Thrift::Union";
+ }
+ if (is_exception) {
+ out << " < ::Thrift::Exception";
+ }
+ out << "; end" << endl << endl;
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_rb_generator::generate_xception(t_struct* txception) {
+ generate_rb_struct(f_types_, txception, true);
+}
+
+/**
+ * Generates a ruby struct
+ */
+void t_rb_generator::generate_rb_struct(t_rb_ofstream& out,
+ t_struct* tstruct,
+ bool is_exception = false) {
+ generate_rdoc(out, tstruct);
+ out.indent() << "class " << type_name(tstruct);
+ if (is_exception) {
+ out << " < ::Thrift::Exception";
+ }
+ out << endl;
+
+ out.indent_up();
+ out.indent() << "include ::Thrift::Struct, ::Thrift::Struct_Union" << endl;
+
+ if (is_exception) {
+ generate_rb_simple_exception_constructor(out, tstruct);
+ }
+
+ generate_field_constants(out, tstruct);
+ generate_field_defns(out, tstruct);
+ generate_rb_struct_required_validator(out, tstruct);
+
+ out.indent() << "::Thrift::Struct.generate_accessors self" << endl;
+
+ out.indent_down();
+ out.indent() << "end" << endl << endl;
+}
+
+/**
+ * Generates a ruby union
+ */
+void t_rb_generator::generate_rb_union(t_rb_ofstream& out,
+ t_struct* tstruct,
+ bool is_exception = false) {
+ (void)is_exception;
+ generate_rdoc(out, tstruct);
+ out.indent() << "class " << type_name(tstruct) << " < ::Thrift::Union" << endl;
+
+ out.indent_up();
+ out.indent() << "include ::Thrift::Struct_Union" << endl;
+
+ generate_field_constructors(out, tstruct);
+
+ generate_field_constants(out, tstruct);
+ generate_field_defns(out, tstruct);
+ generate_rb_union_validator(out, tstruct);
+
+ out.indent() << "::Thrift::Union.generate_accessors self" << endl;
+
+ out.indent_down();
+ out.indent() << "end" << endl << endl;
+}
+
+void t_rb_generator::generate_field_constructors(t_rb_ofstream& out, t_struct* tstruct) {
+
+ out.indent() << "class << self" << endl;
+ out.indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (f_iter != fields.begin()) {
+ out << endl;
+ }
+ std::string field_name = (*f_iter)->get_name();
+
+ out.indent() << "def " << field_name << "(val)" << endl;
+ out.indent() << " " << tstruct->get_name() << ".new(:" << field_name << ", val)" << endl;
+ out.indent() << "end" << endl;
+ }
+
+ out.indent_down();
+ out.indent() << "end" << endl;
+
+ out << endl;
+}
+
+void t_rb_generator::generate_rb_simple_exception_constructor(t_rb_ofstream& out,
+ t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+
+ if (members.size() == 1) {
+ vector<t_field*>::const_iterator m_iter = members.begin();
+
+ if ((*m_iter)->get_type()->is_string()) {
+ string name = (*m_iter)->get_name();
+
+ out.indent() << "def initialize(message=nil)" << endl;
+ out.indent_up();
+ out.indent() << "super()" << endl;
+ out.indent() << "self." << name << " = message" << endl;
+ out.indent_down();
+ out.indent() << "end" << endl << endl;
+
+ if (name != "message") {
+ out.indent() << "def message; " << name << " end" << endl << endl;
+ }
+ }
+ }
+}
+
+void t_rb_generator::generate_field_constants(t_rb_ofstream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ std::string field_name = (*f_iter)->get_name();
+ std::string cap_field_name = upcase_string(field_name);
+
+ out.indent() << cap_field_name << " = " << (*f_iter)->get_key() << endl;
+ }
+ out << endl;
+}
+
+void t_rb_generator::generate_field_defns(t_rb_ofstream& out, t_struct* tstruct) {
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out.indent() << "FIELDS = {" << endl;
+ out.indent_up();
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (f_iter != fields.begin()) {
+ out << "," << endl;
+ }
+
+ // generate the field docstrings within the FIELDS constant. no real better place...
+ generate_rdoc(out, *f_iter);
+
+ out.indent() << upcase_string((*f_iter)->get_name()) << " => ";
+
+ generate_field_data(out,
+ (*f_iter)->get_type(),
+ (*f_iter)->get_name(),
+ (*f_iter)->get_value(),
+ (*f_iter)->get_req() == t_field::T_OPTIONAL);
+ }
+ out.indent_down();
+ out << endl;
+ out.indent() << "}" << endl << endl;
+
+ out.indent() << "def struct_fields; FIELDS; end" << endl << endl;
+}
+
+void t_rb_generator::generate_field_data(t_rb_ofstream& out,
+ t_type* field_type,
+ const std::string& field_name = "",
+ t_const_value* field_value = NULL,
+ bool optional = false) {
+ field_type = get_true_type(field_type);
+
+ // Begin this field's defn
+ out << "{:type => " << type_to_enum(field_type);
+
+ if (!field_name.empty()) {
+ out << ", :name => '" << field_name << "'";
+ }
+
+ if (field_value != NULL) {
+ out << ", :default => ";
+ render_const_value(out, field_type, field_value);
+ }
+
+ if (!field_type->is_base_type()) {
+ if (field_type->is_struct() || field_type->is_xception()) {
+ out << ", :class => " << full_type_name((t_struct*)field_type);
+ } else if (field_type->is_list()) {
+ out << ", :element => ";
+ generate_field_data(out, ((t_list*)field_type)->get_elem_type());
+ } else if (field_type->is_map()) {
+ out << ", :key => ";
+ generate_field_data(out, ((t_map*)field_type)->get_key_type());
+ out << ", :value => ";
+ generate_field_data(out, ((t_map*)field_type)->get_val_type());
+ } else if (field_type->is_set()) {
+ out << ", :element => ";
+ generate_field_data(out, ((t_set*)field_type)->get_elem_type());
+ }
+ } else {
+ if (((t_base_type*)field_type)->is_binary()) {
+ out << ", :binary => true";
+ }
+ }
+
+ if (optional) {
+ out << ", :optional => true";
+ }
+
+ if (field_type->is_enum()) {
+ out << ", :enum_class => " << full_type_name(field_type);
+ }
+
+ // End of this field's defn
+ out << "}";
+}
+
+void t_rb_generator::begin_namespace(t_rb_ofstream& out, vector<std::string> modules) {
+ for (auto & module : modules) {
+ out.indent() << "module " << module << endl;
+ out.indent_up();
+ }
+}
+
+void t_rb_generator::end_namespace(t_rb_ofstream& out, vector<std::string> modules) {
+ for (vector<std::string>::reverse_iterator m_iter = modules.rbegin(); m_iter != modules.rend();
+ ++m_iter) {
+ out.indent_down();
+ out.indent() << "end" << endl;
+ }
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_rb_generator::generate_service(t_service* tservice) {
+ string f_service_name = namespace_dir_ + underscore(service_name_) + ".rb";
+ f_service_.open(f_service_name.c_str());
+
+ f_service_ << rb_autogen_comment() << endl << render_require_thrift();
+
+ if (tservice->get_extends() != NULL) {
+ if (namespaced_) {
+ f_service_ << "require '" << rb_namespace_to_path_prefix(
+ tservice->get_extends()->get_program()->get_namespace("rb"))
+ << underscore(tservice->get_extends()->get_name()) << "'" << endl;
+ } else {
+ f_service_ << "require '" << require_prefix_
+ << underscore(tservice->get_extends()->get_name()) << "'" << endl;
+ }
+ }
+
+ f_service_ << "require '" << require_prefix_ << underscore(program_name_) << "_types'" << endl
+ << endl;
+
+ begin_namespace(f_service_, ruby_modules(tservice->get_program()));
+
+ f_service_.indent() << "module " << capitalize(tservice->get_name()) << endl;
+ f_service_.indent_up();
+
+ // Generate the three main parts of the service (well, two for now in PHP)
+ generate_service_client(tservice);
+ generate_service_server(tservice);
+ generate_service_helpers(tservice);
+
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl << endl;
+
+ end_namespace(f_service_, ruby_modules(tservice->get_program()));
+
+ // Close service file
+ f_service_.close();
+}
+
+/**
+ * Generates helper functions for a service.
+ *
+ * @param tservice The service to generate a header definition for
+ */
+void t_rb_generator::generate_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ f_service_.indent() << "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* ts = (*f_iter)->get_arglist();
+ generate_rb_struct(f_service_, ts);
+ generate_rb_function_helpers(*f_iter);
+ }
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_rb_generator::generate_rb_function_helpers(t_function* tfunction) {
+ t_struct result(program_, tfunction->get_name() + "_result");
+ t_field success(tfunction->get_returntype(), "success", 0);
+ if (!tfunction->get_returntype()->is_void()) {
+ result.append(&success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ result.append(*f_iter);
+ }
+ generate_rb_struct(f_service_, &result);
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_rb_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "";
+ if (tservice->get_extends() != NULL) {
+ extends = full_type_name(tservice->get_extends());
+ extends_client = " < " + extends + "::Client ";
+ }
+
+ f_service_.indent() << "class Client" << extends_client << endl;
+ f_service_.indent_up();
+
+ f_service_.indent() << "include ::Thrift::Client" << endl << endl;
+
+ // Generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ t_struct* arg_struct = (*f_iter)->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string funname = (*f_iter)->get_name();
+
+ // Open function
+ f_service_.indent() << "def " << function_signature(*f_iter) << endl;
+ f_service_.indent_up();
+ f_service_.indent() << "send_" << funname << "(";
+
+ bool first = true;
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << (*fld_iter)->get_name();
+ }
+ f_service_ << ")" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_service_.indent();
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_ << "return ";
+ }
+ f_service_ << "recv_" << funname << "()" << endl;
+ }
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl;
+ f_service_ << endl;
+
+ f_service_.indent() << "def send_" << function_signature(*f_iter) << endl;
+ f_service_.indent_up();
+
+ std::string argsname = capitalize((*f_iter)->get_name() + "_args");
+ std::string messageSendProc = (*f_iter)->is_oneway() ? "send_oneway_message" : "send_message";
+
+ f_service_.indent() << messageSendProc << "('" << funname << "', " << argsname;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ f_service_ << ", :" << (*fld_iter)->get_name() << " => " << (*fld_iter)->get_name();
+ }
+
+ f_service_ << ")" << endl;
+
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ std::string resultname = capitalize((*f_iter)->get_name() + "_result");
+ t_struct noargs(program_);
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ // Open function
+ f_service_ << endl;
+ f_service_.indent() << "def " << function_signature(&recv_function) << endl;
+ f_service_.indent_up();
+
+ // TODO(mcslee): Validate message reply here, seq ids etc.
+
+ f_service_.indent() << "result = receive_message(" << resultname << ")" << endl;
+
+ // Careful, only return _result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ f_service_.indent() << "return result.success unless result.success.nil?" << endl;
+ }
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_.indent() << "raise result." << (*x_iter)->get_name() << " unless result."
+ << (*x_iter)->get_name() << ".nil?" << endl;
+ }
+
+ // Careful, only return _result if not a void function
+ if ((*f_iter)->get_returntype()->is_void()) {
+ f_service_.indent() << "return" << endl;
+ } else {
+ f_service_.indent() << "raise "
+ "::Thrift::ApplicationException.new(::Thrift::ApplicationException::"
+ "MISSING_RESULT, '" << (*f_iter)->get_name()
+ << " failed: unknown result')" << endl;
+ }
+
+ // Close function
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl << endl;
+ }
+ }
+
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl << endl;
+}
+
+/**
+ * Generates a service server definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_rb_generator::generate_service_server(t_service* tservice) {
+ // Generate the dispatch methods
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ string extends = "";
+ string extends_processor = "";
+ if (tservice->get_extends() != NULL) {
+ extends = full_type_name(tservice->get_extends());
+ extends_processor = " < " + extends + "::Processor ";
+ }
+
+ // Generate the header portion
+ f_service_.indent() << "class Processor" << extends_processor << endl;
+ f_service_.indent_up();
+
+ f_service_.indent() << "include ::Thrift::Processor" << endl << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
+
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl << endl;
+}
+
+/**
+ * Generates a process function definition.
+ *
+ * @param tfunction The function to write a dispatcher for
+ */
+void t_rb_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
+ (void)tservice;
+ // Open function
+ f_service_.indent() << "def process_" << tfunction->get_name() << "(seqid, iprot, oprot)" << endl;
+ f_service_.indent_up();
+
+ string argsname = capitalize(tfunction->get_name()) + "_args";
+ string resultname = capitalize(tfunction->get_name()) + "_result";
+
+ f_service_.indent() << "args = read_args(iprot, " << argsname << ")" << endl;
+
+ t_struct* xs = tfunction->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_.indent() << "result = " << resultname << ".new()" << endl;
+ }
+
+ // Try block for a function with exceptions
+ if (xceptions.size() > 0) {
+ f_service_.indent() << "begin" << endl;
+ f_service_.indent_up();
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_.indent();
+ if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
+ f_service_ << "result.success = ";
+ }
+ f_service_ << "@handler." << tfunction->get_name() << "(";
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+ f_service_ << ")" << endl;
+
+ if (!tfunction->is_oneway() && xceptions.size() > 0) {
+ f_service_.indent_down();
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ f_service_.indent() << "rescue " << full_type_name((*x_iter)->get_type()) << " => "
+ << (*x_iter)->get_name() << endl;
+ if (!tfunction->is_oneway()) {
+ f_service_.indent_up();
+ f_service_.indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name()
+ << endl;
+ f_service_.indent_down();
+ }
+ }
+ f_service_.indent() << "end" << endl;
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_.indent() << "return" << endl;
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl << endl;
+ return;
+ }
+
+ f_service_.indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)"
+ << endl;
+
+ // Close function
+ f_service_.indent_down();
+ f_service_.indent() << "end" << endl << endl;
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_rb_generator::function_signature(t_function* tfunction, string prefix) {
+ // TODO(mcslee): Nitpicky, no ',' if argument_list is empty
+ return prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")";
+}
+
+/**
+ * Renders a field list
+ */
+string t_rb_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += ", ";
+ }
+ result += (*f_iter)->get_name();
+ }
+ return result;
+}
+
+string t_rb_generator::type_name(const t_type* ttype) {
+ string prefix = "";
+
+ string name = ttype->get_name();
+ if (ttype->is_struct() || ttype->is_xception() || ttype->is_enum()) {
+ name = capitalize(ttype->get_name());
+ }
+
+ return prefix + name;
+}
+
+string t_rb_generator::full_type_name(const t_type* ttype) {
+ string prefix = "::";
+ vector<std::string> modules = ruby_modules(ttype->get_program());
+ for (auto & module : modules) {
+ prefix += module + "::";
+ }
+ return prefix + type_name(ttype);
+}
+
+/**
+ * Converts the parse type to a Ruby tyoe
+ */
+string t_rb_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "::Thrift::Types::STRING";
+ case t_base_type::TYPE_BOOL:
+ return "::Thrift::Types::BOOL";
+ case t_base_type::TYPE_I8:
+ return "::Thrift::Types::BYTE";
+ case t_base_type::TYPE_I16:
+ return "::Thrift::Types::I16";
+ case t_base_type::TYPE_I32:
+ return "::Thrift::Types::I32";
+ case t_base_type::TYPE_I64:
+ return "::Thrift::Types::I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "::Thrift::Types::DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return "::Thrift::Types::I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "::Thrift::Types::STRUCT";
+ } else if (type->is_map()) {
+ return "::Thrift::Types::MAP";
+ } else if (type->is_set()) {
+ return "::Thrift::Types::SET";
+ } else if (type->is_list()) {
+ return "::Thrift::Types::LIST";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+string t_rb_generator::rb_namespace_to_path_prefix(string rb_namespace) {
+ string namespaces_left = rb_namespace;
+ string::size_type loc;
+
+ string path_prefix = "";
+
+ while ((loc = namespaces_left.find(".")) != string::npos) {
+ path_prefix = path_prefix + underscore(namespaces_left.substr(0, loc)) + "/";
+ namespaces_left = namespaces_left.substr(loc + 1);
+ }
+ if (namespaces_left.size() > 0) {
+ path_prefix = path_prefix + underscore(namespaces_left) + "/";
+ }
+ return path_prefix;
+}
+
+void t_rb_generator::generate_rdoc(t_rb_ofstream& out, t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ out.indent();
+ generate_docstring_comment(out, "", "# ", tdoc->get_doc(), "");
+ }
+}
+
+void t_rb_generator::generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct) {
+ out.indent() << "def validate" << endl;
+ out.indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+ if (field->get_req() == t_field::T_REQUIRED) {
+ out.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
+ "'Required field " << field->get_name() << " is unset!')";
+ if (field->get_type()->is_bool()) {
+ out << " if @" << field->get_name() << ".nil?";
+ } else {
+ out << " unless @" << field->get_name();
+ }
+ out << endl;
+ }
+ }
+
+ // if field is an enum, check that its value is valid
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field* field = (*f_iter);
+
+ if (field->get_type()->is_enum()) {
+ out.indent() << "unless @" << field->get_name() << ".nil? || "
+ << full_type_name(field->get_type()) << "::VALID_VALUES.include?(@"
+ << field->get_name() << ")" << endl;
+ out.indent_up();
+ out.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
+ "'Invalid value of field " << field->get_name() << "!')" << endl;
+ out.indent_down();
+ out.indent() << "end" << endl;
+ }
+ }
+
+ out.indent_down();
+ out.indent() << "end" << endl << endl;
+}
+
+void t_rb_generator::generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct) {
+ out.indent() << "def validate" << endl;
+ out.indent_up();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out.indent()
+ << "raise(StandardError, 'Union fields are not set.') if get_set_field.nil? || get_value.nil?"
+ << endl;
+
+ // if field is an enum, check that its value is valid
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ const t_field* field = (*f_iter);
+
+ if (field->get_type()->is_enum()) {
+ out.indent() << "if get_set_field == :" << field->get_name() << endl;
+ out.indent() << " raise "
+ "::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, "
+ "'Invalid value of field " << field->get_name() << "!') unless "
+ << full_type_name(field->get_type()) << "::VALID_VALUES.include?(get_value)"
+ << endl;
+ out.indent() << "end" << endl;
+ }
+ }
+
+ out.indent_down();
+ out.indent() << "end" << endl << endl;
+}
+
+THRIFT_REGISTER_GENERATOR(
+ rb,
+ "Ruby",
+ " rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n"
+ " namespaced: Generate files in idiomatic namespaced directories.\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rs_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rs_generator.cc
new file mode 100644
index 000000000..70e18472c
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_rs_generator.cc
@@ -0,0 +1,3351 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::vector;
+using std::set;
+
+static const string endl("\n"); // avoid ostream << std::endl flushes
+static const string SERVICE_RESULT_VARIABLE("result_value");
+static const string RESULT_STRUCT_SUFFIX("Result");
+static const string RUST_RESERVED_WORDS[] = {
+ "abstract", "alignof", "as", "become",
+ "box", "break", "const", "continue",
+ "crate", "do", "else", "enum",
+ "extern", "false", "final", "fn",
+ "for", "if", "impl", "in",
+ "let", "loop", "macro", "match",
+ "mod", "move", "mut", "offsetof",
+ "override", "priv", "proc", "pub",
+ "pure", "ref", "return", "Self",
+ "self", "sizeof", "static", "struct",
+ "super", "trait", "true", "type",
+ "typeof", "unsafe", "unsized", "use",
+ "virtual", "where", "while", "yield"
+};
+const set<string> RUST_RESERVED_WORDS_SET(
+ RUST_RESERVED_WORDS,
+ RUST_RESERVED_WORDS + sizeof(RUST_RESERVED_WORDS)/sizeof(RUST_RESERVED_WORDS[0])
+);
+
+static const string SYNC_CLIENT_GENERIC_BOUND_VARS("<IP, OP>");
+static const string SYNC_CLIENT_GENERIC_BOUNDS("where IP: TInputProtocol, OP: TOutputProtocol");
+
+// FIXME: extract common TMessageIdentifier function
+// FIXME: have to_rust_type deal with Option
+
+class t_rs_generator : public t_generator {
+public:
+ t_rs_generator(
+ t_program* program,
+ const std::map<std::string, std::string>&,
+ const std::string&
+ ) : t_generator(program) {
+ gen_dir_ = get_out_dir();
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+private:
+ // struct type
+ // T_REGULAR: user-defined struct in the IDL
+ // T_ARGS: struct used to hold all service-call parameters
+ // T_RESULT: struct used to hold all service-call returns and exceptions
+ // T_EXCEPTION: user-defined exception in the IDL
+ enum e_struct_type { T_REGULAR, T_ARGS, T_RESULT, T_EXCEPTION };
+
+ // Directory to which generated code is written.
+ string gen_dir_;
+
+ // File to which generated code is written.
+ ofstream_with_content_based_conditional_update f_gen_;
+
+ // Write the common compiler attributes and module includes to the top of the auto-generated file.
+ void render_attributes_and_includes();
+
+ // Create the closure of Rust modules referenced by this service.
+ void compute_service_referenced_modules(t_service *tservice, set<string> &referenced_modules);
+
+ // Write the rust representation of an enum.
+ void render_enum_definition(t_enum* tenum, const string& enum_name);
+
+ // Write the impl blocks associated with the traits necessary to convert an enum to/from an i32.
+ void render_enum_conversion(t_enum* tenum, const string& enum_name);
+
+ // Write the impl block associated with the rust representation of an enum. This includes methods
+ // to write the enum to a protocol, read it from a protocol, etc.
+ void render_enum_impl(const string& enum_name);
+
+ // Write a simple rust const value (ie. `pub const FOO: foo...`).
+ void render_const_value(const string& name, t_type* ttype, t_const_value* tvalue);
+
+ // Write a constant list, set, map or struct. These constants require allocation and cannot be defined
+ // using a 'pub const'. As a result, I create a holder struct with a single `const_value` method that
+ // returns the initialized instance.
+ void render_const_value_holder(const string& name, t_type* ttype, t_const_value* tvalue);
+
+ // Write the actual const value - the right side of a const definition.
+ void render_const_value(t_type* ttype, t_const_value* tvalue, bool is_owned = true);
+
+ // Write a const struct (returned from `const_value` method).
+ void render_const_struct(t_type* ttype, t_const_value* tvalue);
+
+ // Write a const list (returned from `const_value` method).
+ void render_const_list(t_type* ttype, t_const_value* tvalue);
+
+ // Write a const set (returned from `const_value` method).
+ void render_const_set(t_type* ttype, t_const_value* tvalue);
+
+ // Write a const map (returned from `const_value` method).
+ void render_const_map(t_type* ttype, t_const_value* tvalue);
+
+ // Write the code to insert constant values into a rust vec or set. The
+ // `insert_function` is the rust function that we'll use to insert the elements.
+ void render_container_const_value(
+ const string& insert_function,
+ t_type* ttype,
+ t_const_value* tvalue
+ );
+
+ // Write the rust representation of a thrift struct to the generated file. Set `struct_type` to `T_ARGS`
+ // if rendering the struct used to pack arguments for a service call. When `struct_type` is `T_ARGS` the
+ // struct and its members have module visibility, and all fields are required. When `struct_type` is
+ // anything else the struct and its members have public visibility and fields have the visibility set
+ // in their definition.
+ void render_struct(const string& struct_name, t_struct* tstruct, t_rs_generator::e_struct_type struct_type);
+
+ // Write the comment block preceding a type definition (and implementation).
+ void render_type_comment(const string& struct_name);
+
+ // Write the rust representation of a thrift struct. Supports argument structs, result structs,
+ // user-defined structs and exception structs. The exact struct type to be generated is controlled
+ // by the `struct_type` parameter, which (among other things) modifies the visibility of the
+ // written struct and members, controls which trait implementations are generated.
+ void render_struct_definition(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+ );
+
+ // Writes the impl block associated with the rust representation of a struct. At minimum this
+ // contains the methods to read from a protocol and write to a protocol. Additional methods may
+ // be generated depending on `struct_type`.
+ void render_struct_impl(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+ );
+
+ // Generate a `fn new(...)` for a struct with name `struct_name` and type `t_struct`. The auto-generated
+ // code may include generic type parameters to make the constructor more ergonomic. `struct_type` controls
+ // the visibility of the generated constructor.
+ void render_struct_constructor(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+ );
+
+ // Write the `ok_or` method added to all Thrift service call result structs. You can use this method
+ // to convert a struct into a `Result` and use it in a `try!` or combinator chain.
+ void render_result_struct_to_result_method(t_struct* tstruct);
+
+ // Write the implementations for the `Error` and `Debug` traits. These traits are necessary for a
+ // user-defined exception to be properly handled as Rust errors.
+ void render_exception_struct_error_trait_impls(const string& struct_name, t_struct* tstruct);
+
+ // Write the implementations for the `Default`. This trait allows you to specify only the fields you want
+ // and use `..Default::default()` to fill in the rest.
+ void render_struct_default_trait_impl(const string& struct_name, t_struct* tstruct);
+
+ // Write the function that serializes a struct to its wire representation. If `struct_type` is `T_ARGS`
+ // then all fields are considered "required", if not, the default optionality is used.
+ void render_struct_sync_write(t_struct *tstruct, t_rs_generator::e_struct_type struct_type);
+
+ // Helper function that serializes a single struct field to its wire representation. Unpacks the
+ // variable (since it may be optional) and serializes according to the optionality rules required by `req`.
+ // Variables in auto-generated code are passed by reference. Since this function may be called in
+ // contexts where the variable is *already* a reference you can set `field_var_is_ref` to `true` to avoid
+ // generating an extra, unnecessary `&` that the compiler will have to automatically dereference.
+ void render_struct_field_sync_write(
+ const string &field_var,
+ bool field_var_is_ref,
+ t_field *tfield,
+ t_field::e_req req);
+
+ // Write the rust function that serializes a single type (i.e. a i32 etc.) to its wire representation.
+ // Variables in auto-generated code are passed by reference. Since this function may be called in
+ // contexts where the variable is *already* a reference you can set `type_var_is_ref` to `true` to avoid
+ // generating an extra, unnecessary `&` that the compiler will have to automatically dereference.
+ void render_type_sync_write(const string &type_var, bool type_var_is_ref, t_type *ttype);
+
+ // Write a list to the output protocol. `list_variable` is the variable containing the list
+ // that will be written to the output protocol.
+ // Variables in auto-generated code are passed by reference. Since this function may be called in
+ // contexts where the variable is *already* a reference you can set `list_var_is_ref` to `true` to avoid
+ // generating an extra, unnecessary `&` that the compiler will have to automatically dereference.
+ void render_list_sync_write(const string &list_var, bool list_var_is_ref, t_list *tlist);
+
+ // Write a set to the output protocol. `set_variable` is the variable containing the set that will
+ // be written to the output protocol.
+ // Variables in auto-generated code are passed by reference. Since this function may be called in
+ // contexts where the variable is *already* a reference you can set `set_var_is_ref` to `true` to avoid
+ // generating an extra, unnecessary `&` that the compiler will have to automatically dereference.
+ void render_set_sync_write(const string &set_var, bool set_var_is_ref, t_set *tset);
+
+ // Write a map to the output protocol. `map_variable` is the variable containing the map that will
+ // be written to the output protocol.
+ // Variables in auto-generated code are passed by reference. Since this function may be called in
+ // contexts where the variable is *already* a reference you can set `map_var_is_ref` to `true` to avoid
+ // generating an extra, unnecessary `&` that the compiler will have to automatically dereference.
+ void render_map_sync_write(const string &map_var, bool map_var_is_ref, t_map *tset);
+
+ // Return `true` if we need to dereference ths type when writing an element from a container.
+ // Iterations on rust containers are performed as follows: `for v in &values { ... }`
+ // where `v` has type `&RUST_TYPE` All defined functions take primitives by value, so, if the
+ // rendered code is calling such a function it has to dereference `v`.
+ bool needs_deref_on_container_write(t_type* ttype);
+
+ // Return the variable (including all dereferences) required to write values from a rust container
+ // to the output protocol. For example, if you were iterating through a container and using the temp
+ // variable `v` to represent each element, then `ttype` is the type stored in the container and
+ // `base_var` is "v". The return value is the actual string you will have to use to properly reference
+ // the temp variable for writing to the output protocol.
+ string string_container_write_variable(t_type* ttype, const string& base_var);
+
+ // Write the code to read bytes from the wire into the given `t_struct`. `struct_name` is the
+ // actual Rust name of the `t_struct`. If `struct_type` is `T_ARGS` then all struct fields are
+ // necessary. Otherwise, the field's default optionality is used.
+ void render_struct_sync_read(const string &struct_name, t_struct *tstruct, t_rs_generator::e_struct_type struct_type);
+
+ // Write the rust function that deserializes a single type (i.e. i32 etc.) from its wire representation.
+ // Set `is_boxed` to `true` if the resulting value should be wrapped in a `Box::new(...)`.
+ void render_type_sync_read(const string &type_var, t_type *ttype, bool is_boxed = false);
+
+ // Read the wire representation of a list and convert it to its corresponding rust implementation.
+ // The deserialized list is stored in `list_variable`.
+ void render_list_sync_read(t_list *tlist, const string &list_variable);
+
+ // Read the wire representation of a set and convert it to its corresponding rust implementation.
+ // The deserialized set is stored in `set_variable`.
+ void render_set_sync_read(t_set *tset, const string &set_variable);
+
+ // Read the wire representation of a map and convert it to its corresponding rust implementation.
+ // The deserialized map is stored in `map_variable`.
+ void render_map_sync_read(t_map *tmap, const string &map_variable);
+
+ // Return a temporary variable used to store values when deserializing nested containers.
+ string struct_field_read_temp_variable(t_field* tfield);
+
+ // Top-level function that calls the various render functions necessary to write the rust representation
+ // of a thrift union (i.e. an enum).
+ void render_union(t_struct* tstruct);
+
+ // Write the enum corresponding to the Thrift union.
+ void render_union_definition(const string& union_name, t_struct* tstruct);
+
+ // Write the enum impl (with read/write functions) for the Thrift union.
+ void render_union_impl(const string& union_name, t_struct* tstruct);
+
+ // Write the `ENUM::write_to_out_protocol` function.
+ void render_union_sync_write(const string &union_name, t_struct *tstruct);
+
+ // Write the `ENUM::read_from_in_protocol` function.
+ void render_union_sync_read(const string &union_name, t_struct *tstruct);
+
+ // Top-level function that calls the various render functions necessary to write the rust representation
+ // of a Thrift client.
+ void render_sync_client(t_service* tservice);
+
+ // Write the trait with the service-call methods for `tservice`.
+ void render_sync_client_trait(t_service *tservice);
+
+ // Write the trait to be implemented by the client impl if end users can use it to make service calls.
+ void render_sync_client_marker_trait(t_service *tservice);
+
+ // Write the code to create the Thrift service sync client struct and its matching 'impl' block.
+ void render_sync_client_definition_and_impl(const string& client_impl_name);
+
+ // Write the code to create the `SyncClient::new` functions as well as any other functions
+ // callers would like to use on the Thrift service sync client.
+ void render_sync_client_lifecycle_functions(const string& client_struct);
+
+ // Write the code to create the impl block for the `TThriftClient` trait. Since generated
+ // Rust Thrift clients perform all their operations using methods defined in this trait, we
+ // have to implement it for the client structs.
+ void render_sync_client_tthriftclient_impl(const string &client_impl_name);
+
+ // Write the marker traits for any service(s) being extended, including the one for the current
+ // service itself (i.e. `tservice`)
+ void render_sync_client_marker_trait_impls(t_service *tservice, const string &impl_struct_name);
+
+ // Generate a list of all the traits this Thrift client struct extends.
+ string sync_client_marker_traits_for_extension(t_service *tservice);
+
+ // Top-level function that writes the code to make the Thrift service calls.
+ void render_sync_client_process_impl(t_service* tservice);
+
+ // Write the actual function that calls out to the remote service and processes its response.
+ void render_sync_send_recv_wrapper(t_function* tfunc);
+
+ // Write the `send` functionality for a Thrift service call represented by a `t_service->t_function`.
+ void render_sync_send(t_function* tfunc);
+
+ // Write the `recv` functionality for a Thrift service call represented by a `t_service->t_function`.
+ // This method is only rendered if the function is *not* oneway.
+ void render_sync_recv(t_function* tfunc);
+
+ void render_sync_processor(t_service *tservice);
+
+ void render_sync_handler_trait(t_service *tservice);
+ void render_sync_processor_definition_and_impl(t_service *tservice);
+ void render_sync_process_delegation_functions(t_service *tservice);
+ void render_sync_process_function(t_function *tfunc, const string &handler_type);
+ void render_process_match_statements(t_service* tservice);
+ void render_sync_handler_succeeded(t_function *tfunc);
+ void render_sync_handler_failed(t_function *tfunc);
+ void render_sync_handler_failed_user_exception_branch(t_function *tfunc);
+ void render_sync_handler_failed_application_exception_branch(t_function *tfunc, const string &app_err_var);
+ void render_sync_handler_failed_default_exception_branch(t_function *tfunc);
+ void render_sync_handler_send_exception_response(t_function *tfunc, const string &err_var);
+ void render_service_call_structs(t_service* tservice);
+ void render_service_call_args_struct(t_function* tfunc);
+ void render_service_call_result_value_struct(t_function* tfunc);
+
+ string handler_successful_return_struct(t_function* tfunc);
+
+ // Writes the result of `render_thrift_error_struct` wrapped in an `Err(thrift::Error(...))`.
+ void render_thrift_error(
+ const string& error_kind,
+ const string& error_struct,
+ const string& sub_error_kind,
+ const string& error_message
+ );
+
+ // Write a thrift::Error variant struct. Error structs take the form:
+ // ```
+ // pub struct error_struct {
+ // kind: sub_error_kind,
+ // message: error_message,
+ // }
+ // ```
+ // A concrete example is:
+ // ```
+ // pub struct ApplicationError {
+ // kind: ApplicationErrorKind::Unknown,
+ // message: "This is some error message",
+ // }
+ // ```
+ void render_thrift_error_struct(
+ const string& error_struct,
+ const string& sub_error_kind,
+ const string& error_message
+ );
+
+ // Return a string containing all the unpacked service call args given a service call function
+ // `t_function`. Prepends the args with either `&mut self` or `&self` and includes the arg types
+ // in the returned string, for example:
+ // `fn foo(&mut self, field_0: String)`.
+ string rust_sync_service_call_declaration(t_function* tfunc, bool self_is_mutable);
+
+ // Return a string containing all the unpacked service call args given a service call function
+ // `t_function`. Only includes the arg names, each of which is prefixed with the optional prefix
+ // `field_prefix`, for example: `self.field_0`.
+ string rust_sync_service_call_invocation(t_function* tfunc, const string& field_prefix = "");
+
+ // Return a string containing all fields in the struct `tstruct` for use in a function declaration.
+ // Each field is followed by its type, for example: `field_0: String`.
+ string struct_to_declaration(t_struct* tstruct, t_rs_generator::e_struct_type struct_type);
+
+ // Return a string containing all fields in the struct `tstruct` for use in a function call,
+ // for example: `field_0: String`.
+ string struct_to_invocation(t_struct* tstruct, const string& field_prefix = "");
+
+ // Write the documentation for a struct, service-call or other documentation-annotated element.
+ void render_rustdoc(t_doc* tdoc);
+
+ // Return `true` if the true type of `ttype` is a thrift double, `false` otherwise.
+ bool is_double(t_type* ttype);
+
+ // Return a string representing the rust type given a `t_type`.
+ string to_rust_type(t_type* ttype, bool ordered_float = true);
+
+ // Return a string representing the `const` rust type given a `t_type`
+ string to_rust_const_type(t_type* ttype, bool ordered_float = true);
+
+ // Return a string representing the rift `protocol::TType` given a `t_type`.
+ string to_rust_field_type_enum(t_type* ttype);
+
+ // Return the default value to be used when initializing a struct field which has `OPT_IN_REQ_OUT`
+ // optionality.
+ string opt_in_req_out_value(t_type* ttype);
+
+ // Return `true` if we can write a const of the form `pub const FOO: ...`.
+ bool can_generate_simple_const(t_type* ttype);
+
+ // Return `true` if we cannot write a standard Rust constant (because the type needs some allocation).
+ bool can_generate_const_holder(t_type* ttype);
+
+ // Return `true` if this type is a void, and should be represented by the rust `()` type.
+ bool is_void(t_type* ttype);
+
+ t_field::e_req actual_field_req(t_field* tfield, t_rs_generator::e_struct_type struct_type);
+
+ // Return `true` if this `t_field::e_req` is either `t_field::T_OPTIONAL` or `t_field::T_OPT_IN_REQ_OUT`
+ // and needs to be wrapped by an `Option<TYPE_NAME>`, `false` otherwise.
+ bool is_optional(t_field::e_req req);
+
+ // Return `true` if the service call has arguments, `false` otherwise.
+ bool has_args(t_function* tfunc);
+
+ // Return `true` if a service call has non-`()` arguments, `false` otherwise.
+ bool has_non_void_args(t_function* tfunc);
+
+ // Return `pub ` (notice trailing whitespace!) if the struct should be public, `` (empty string) otherwise.
+ string visibility_qualifier(t_rs_generator::e_struct_type struct_type);
+
+ // Returns the namespace prefix for a given Thrift service. If the type is defined in the presently-computed
+ // Thrift program, then an empty string is returned.
+ string rust_namespace(t_service* tservice);
+
+ // Returns the namespace prefix for a given Thrift type. If the type is defined in the presently-computed
+ // Thrift program, then an empty string is returned.
+ string rust_namespace(t_type* ttype);
+
+ // Returns the camel-cased name for a Rust struct type. Handles the case where `tstruct->get_name()` is
+ // a reserved word.
+ string rust_struct_name(t_struct* tstruct);
+
+ // Returns the snake-cased name for a Rust field or local variable. Handles the case where
+ // `tfield->get_name()` is a reserved word.
+ string rust_field_name(t_field* tstruct);
+
+ // Returns the camel-cased name for a Rust union type. Handles the case where `tstruct->get_name()` is
+ // a reserved word.
+ string rust_union_field_name(t_field* tstruct);
+
+ // Converts any variable name into a 'safe' variant that does not clash with any Rust reserved keywords.
+ string rust_safe_name(const string& name);
+
+ // Return `true` if the name is a reserved Rust keyword, `false` otherwise.
+ bool is_reserved(const string& name);
+
+ // Return the name of the function that users will invoke to make outgoing service calls.
+ string service_call_client_function_name(t_function* tfunc);
+
+ // Return the name of the function that users will have to implement to handle incoming service calls.
+ string service_call_handler_function_name(t_function* tfunc);
+
+ // Return the name of the struct used to pack the arguments for the thrift service call.
+ string service_call_args_struct_name(t_function* tfunc);
+
+ // Return the name of the struct used to pack the return value
+ // and user-defined exceptions for the thrift service call.
+ string service_call_result_struct_name(t_function* tfunc);
+
+ string rust_sync_client_marker_trait_name(t_service* tservice);
+
+ // Return the trait name for the sync service client given a `t_service`.
+ string rust_sync_client_trait_name(t_service* tservice);
+
+ // Return the name for the sync service client struct given a `t_service`.
+ string rust_sync_client_impl_name(t_service* tservice);
+
+ // Return the trait name that users will have to implement for the server half of a Thrift service.
+ string rust_sync_handler_trait_name(t_service* tservice);
+
+ // Return the struct name for the server half of a Thrift service.
+ string rust_sync_processor_name(t_service* tservice);
+
+ // Return the struct name for the struct that contains all the service-call implementations for
+ // the server half of a Thrift service.
+ string rust_sync_processor_impl_name(t_service *tservice);
+
+ // Return the variant name for an enum variant
+ string rust_enum_variant_name(const string& name);
+
+ // Properly uppercase names for use in Rust.
+ string rust_upper_case(const string& name);
+
+ // Snake-case field, parameter and function names and make them Rust friendly.
+ string rust_snake_case(const string& name);
+
+ // Camel-case type/variant names and make them Rust friendly.
+ string rust_camel_case(const string& name);
+
+ // Replace all instances of `search_string` with `replace_string` in `target`.
+ void string_replace(string& target, const string& search_string, const string& replace_string);
+
+ // Adjust field identifier to correctly handle unspecified field identifiers
+ // THRIFT-4953
+ string rust_safe_field_id(int32_t id);
+};
+
+void t_rs_generator::init_generator() {
+ // make output directory for this thrift program
+ MKDIR(gen_dir_.c_str());
+
+ // create the file into which we're going to write the generated code
+ string f_gen_name = gen_dir_ + "/" + rust_snake_case(get_program()->get_name()) + ".rs";
+ f_gen_.open(f_gen_name.c_str());
+
+ // header comment
+ f_gen_ << "// " << autogen_summary() << endl;
+ f_gen_ << "// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING" << endl;
+ f_gen_ << endl;
+
+ render_attributes_and_includes();
+}
+
+void t_rs_generator::render_attributes_and_includes() {
+ // turn off some compiler/clippy warnings
+
+ // code always includes BTreeMap/BTreeSet/OrderedFloat
+ f_gen_ << "#![allow(unused_imports)]" << endl;
+ // code might not include imports from crates
+ f_gen_ << "#![allow(unused_extern_crates)]" << endl;
+ // constructors take *all* struct parameters, which can trigger the "too many arguments" warning
+ // some auto-gen'd types can be deeply nested. clippy recommends factoring them out which is hard to autogen
+ f_gen_ << "#![cfg_attr(feature = \"cargo-clippy\", allow(too_many_arguments, type_complexity))]" << endl;
+ // prevent rustfmt from running against this file
+ // lines are too long, code is (thankfully!) not visual-indented, etc.
+ f_gen_ << "#![cfg_attr(rustfmt, rustfmt_skip)]" << endl;
+ f_gen_ << endl;
+
+ // add standard includes
+ f_gen_ << "extern crate thrift;" << endl;
+ f_gen_ << endl;
+ f_gen_ << "use thrift::OrderedFloat;" << endl;
+ f_gen_ << "use std::cell::RefCell;" << endl;
+ f_gen_ << "use std::collections::{BTreeMap, BTreeSet};" << endl;
+ f_gen_ << "use std::convert::{From, TryFrom};" << endl;
+ f_gen_ << "use std::default::Default;" << endl;
+ f_gen_ << "use std::error::Error;" << endl;
+ f_gen_ << "use std::fmt;" << endl;
+ f_gen_ << "use std::fmt::{Display, Formatter};" << endl;
+ f_gen_ << "use std::rc::Rc;" << endl;
+ f_gen_ << endl;
+ f_gen_ << "use thrift::{ApplicationError, ApplicationErrorKind, ProtocolError, ProtocolErrorKind, TThriftClient};" << endl;
+ f_gen_ << "use thrift::protocol::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TInputProtocol, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType};" << endl;
+ f_gen_ << "use thrift::protocol::field_id;" << endl;
+ f_gen_ << "use thrift::protocol::verify_expected_message_type;" << endl;
+ f_gen_ << "use thrift::protocol::verify_expected_sequence_number;" << endl;
+ f_gen_ << "use thrift::protocol::verify_expected_service_call;" << endl;
+ f_gen_ << "use thrift::protocol::verify_required_field_exists;" << endl;
+ f_gen_ << "use thrift::server::TProcessor;" << endl;
+ f_gen_ << endl;
+
+ // add all the program includes
+ // NOTE: this is more involved than you would expect because of service extension
+ // Basically, I have to find the closure of all the services and include their modules at the top-level
+
+ set<string> referenced_modules;
+
+ // first, start by adding explicit thrift includes
+ const vector<t_program*> includes = get_program()->get_includes();
+ vector<t_program*>::const_iterator includes_iter;
+ for(includes_iter = includes.begin(); includes_iter != includes.end(); ++includes_iter) {
+ referenced_modules.insert((*includes_iter)->get_name());
+ }
+
+ // next, recursively iterate through all the services and add the names of any programs they reference
+ const vector<t_service*> services = get_program()->get_services();
+ vector<t_service*>::const_iterator service_iter;
+ for (service_iter = services.begin(); service_iter != services.end(); ++service_iter) {
+ compute_service_referenced_modules(*service_iter, referenced_modules);
+ }
+
+ // finally, write all the "pub use..." declarations
+ if (!referenced_modules.empty()) {
+ set<string>::iterator module_iter;
+ for (module_iter = referenced_modules.begin(); module_iter != referenced_modules.end(); ++module_iter) {
+ f_gen_ << "use " << rust_snake_case(*module_iter) << ";" << endl;
+ }
+ f_gen_ << endl;
+ }
+}
+
+void t_rs_generator::compute_service_referenced_modules(
+ t_service *tservice,
+ set<string> &referenced_modules
+) {
+ t_service* extends = tservice->get_extends();
+ if (extends) {
+ if (extends->get_program() != get_program()) {
+ referenced_modules.insert(extends->get_program()->get_name());
+ }
+ compute_service_referenced_modules(extends, referenced_modules);
+ }
+}
+
+void t_rs_generator::close_generator() {
+ f_gen_.close();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Consts
+//
+// NOTE: consider using macros to generate constants
+//
+//-----------------------------------------------------------------------------
+
+// This is worse than it should be because constants
+// aren't (sensibly) limited to scalar types
+void t_rs_generator::generate_const(t_const* tconst) {
+ string name = tconst->get_name();
+ t_type* ttype = tconst->get_type();
+ t_const_value* tvalue = tconst->get_value();
+
+ if (can_generate_simple_const(ttype)) {
+ render_const_value(name, ttype, tvalue);
+ } else if (can_generate_const_holder(ttype)) {
+ render_const_value_holder(name, ttype, tvalue);
+ } else {
+ throw "cannot generate const for " + name;
+ }
+}
+
+void t_rs_generator::render_const_value(const string& name, t_type* ttype, t_const_value* tvalue) {
+ if (!can_generate_simple_const(ttype)) {
+ throw "cannot generate simple rust constant for " + ttype->get_name();
+ }
+
+ f_gen_ << "pub const " << rust_upper_case(name) << ": " << to_rust_const_type(ttype) << " = ";
+ render_const_value(ttype, tvalue, false);
+ f_gen_ << ";" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_const_value_holder(const string& name, t_type* ttype, t_const_value* tvalue) {
+ if (!can_generate_const_holder(ttype)) {
+ throw "cannot generate constant holder for " + ttype->get_name();
+ }
+
+ string holder_name("Const" + rust_camel_case(name));
+
+ f_gen_ << indent() << "pub struct " << holder_name << ";" << endl;
+ f_gen_ << indent() << "impl " << holder_name << " {" << endl;
+ indent_up();
+
+ f_gen_ << indent() << "pub fn const_value() -> " << to_rust_type(ttype) << " {" << endl;
+ indent_up();
+ render_const_value(ttype, tvalue);
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_const_value(t_type* ttype, t_const_value* tvalue, bool is_owned) {
+ if (ttype->is_base_type()) {
+ t_base_type* tbase_type = (t_base_type*)ttype;
+ switch (tbase_type->get_base()) {
+ case t_base_type::TYPE_STRING:
+ if (tbase_type->is_binary()) {
+ if (is_owned) {
+ f_gen_ << "\"" << tvalue->get_string() << "\""<< ".to_owned().into_bytes()";
+ } else {
+ f_gen_ << "b\"" << tvalue->get_string() << "\"";
+ }
+ } else {
+ f_gen_ << "\"" << tvalue->get_string() << "\"";
+ if (is_owned) {
+ f_gen_ << ".to_owned()";
+ }
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ f_gen_ << (tvalue->get_integer() ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ f_gen_ << tvalue->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ f_gen_ << "OrderedFloat::from(" << tvalue->get_double() << " as f64)";
+ break;
+ default:
+ throw "cannot generate const value for " + t_base_type::t_base_name(tbase_type->get_base());
+ }
+ } else if (ttype->is_typedef()) {
+ render_const_value(get_true_type(ttype), tvalue);
+ } else if (ttype->is_enum()) {
+ f_gen_ << indent() << "{" << endl;
+ indent_up();
+ f_gen_
+ << indent()
+ << to_rust_type(ttype)
+ << "::try_from("
+ << tvalue->get_integer()
+ << ").expect(\"expecting valid const value\")"
+ << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ } else if (ttype->is_struct() || ttype->is_xception()) {
+ render_const_struct(ttype, tvalue);
+ } else if (ttype->is_container()) {
+ f_gen_ << indent() << "{" << endl;
+ indent_up();
+
+ if (ttype->is_list()) {
+ render_const_list(ttype, tvalue);
+ } else if (ttype->is_set()) {
+ render_const_set(ttype, tvalue);
+ } else if (ttype->is_map()) {
+ render_const_map(ttype, tvalue);
+ } else {
+ throw "cannot generate const container value for " + ttype->get_name();
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ } else {
+ throw "cannot generate const value for " + ttype->get_name();
+ }
+}
+
+void t_rs_generator::render_const_struct(t_type* ttype, t_const_value*) {
+ if (((t_struct*)ttype)->is_union()) {
+ f_gen_ << indent() << "{" << endl;
+ indent_up();
+ f_gen_ << indent() << "unimplemented!()" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ } else {
+ f_gen_ << indent() << "{" << endl;
+ indent_up();
+ f_gen_ << indent() << "unimplemented!()" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+}
+
+void t_rs_generator::render_const_list(t_type* ttype, t_const_value* tvalue) {
+ t_type* elem_type = ((t_list*)ttype)->get_elem_type();
+ f_gen_ << indent() << "let mut l: Vec<" << to_rust_type(elem_type) << "> = Vec::new();" << endl;
+ const vector<t_const_value*>& elems = tvalue->get_list();
+ vector<t_const_value*>::const_iterator elem_iter;
+ for(elem_iter = elems.begin(); elem_iter != elems.end(); ++elem_iter) {
+ t_const_value* elem_value = (*elem_iter);
+ render_container_const_value("l.push", elem_type, elem_value);
+ }
+ f_gen_ << indent() << "l" << endl;
+}
+
+void t_rs_generator::render_const_set(t_type* ttype, t_const_value* tvalue) {
+ t_type* elem_type = ((t_set*)ttype)->get_elem_type();
+ f_gen_ << indent() << "let mut s: BTreeSet<" << to_rust_type(elem_type) << "> = BTreeSet::new();" << endl;
+ const vector<t_const_value*>& elems = tvalue->get_list();
+ vector<t_const_value*>::const_iterator elem_iter;
+ for(elem_iter = elems.begin(); elem_iter != elems.end(); ++elem_iter) {
+ t_const_value* elem_value = (*elem_iter);
+ render_container_const_value("s.insert", elem_type, elem_value);
+ }
+ f_gen_ << indent() << "s" << endl;
+}
+
+void t_rs_generator::render_const_map(t_type* ttype, t_const_value* tvalue) {
+ t_type* key_type = ((t_map*)ttype)->get_key_type();
+ t_type* val_type = ((t_map*)ttype)->get_val_type();
+ f_gen_
+ << indent()
+ << "let mut m: BTreeMap<"
+ << to_rust_type(key_type) << ", " << to_rust_type(val_type)
+ << "> = BTreeMap::new();"
+ << endl;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& elems = tvalue->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator elem_iter;
+ for (elem_iter = elems.begin(); elem_iter != elems.end(); ++elem_iter) {
+ t_const_value* key_value = elem_iter->first;
+ t_const_value* val_value = elem_iter->second;
+ if (get_true_type(key_type)->is_base_type()) {
+ f_gen_ << indent() << "let k = ";
+ render_const_value(key_type, key_value);
+ f_gen_ << ";" << endl;
+ } else {
+ f_gen_ << indent() << "let k = {" << endl;
+ indent_up();
+ render_const_value(key_type, key_value);
+ indent_down();
+ f_gen_ << indent() << "};" << endl;
+ }
+ if (get_true_type(val_type)->is_base_type()) {
+ f_gen_ << indent() << "let v = ";
+ render_const_value(val_type, val_value);
+ f_gen_ << ";" << endl;
+ } else {
+ f_gen_ << indent() << "let v = {" << endl;
+ indent_up();
+ render_const_value(val_type, val_value);
+ indent_down();
+ f_gen_ << indent() << "};" << endl;
+ }
+ f_gen_ << indent() << "m.insert(k, v);" << endl;
+ }
+ f_gen_ << indent() << "m" << endl;
+}
+
+void t_rs_generator::render_container_const_value(
+ const string& insert_function,
+ t_type* ttype,
+ t_const_value* tvalue
+) {
+ if (get_true_type(ttype)->is_base_type()) {
+ f_gen_ << indent() << insert_function << "(";
+ render_const_value(ttype, tvalue);
+ f_gen_ << ");" << endl;
+ } else {
+ f_gen_ << indent() << insert_function << "(" << endl;
+ indent_up();
+ render_const_value(ttype, tvalue);
+ indent_down();
+ f_gen_ << indent() << ");" << endl;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Typedefs
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::generate_typedef(t_typedef* ttypedef) {
+ std::string actual_type = to_rust_type(ttypedef->get_type());
+ f_gen_ << "pub type " << rust_safe_name(ttypedef->get_symbolic()) << " = " << actual_type << ";" << endl;
+ f_gen_ << endl;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Enums
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::generate_enum(t_enum* tenum) {
+ string enum_name(rust_camel_case(tenum->get_name()));
+ render_enum_definition(tenum, enum_name);
+ render_enum_impl(enum_name);
+ render_enum_conversion(tenum, enum_name);
+}
+
+void t_rs_generator::render_enum_definition(t_enum* tenum, const string& enum_name) {
+ render_rustdoc((t_doc*) tenum);
+ f_gen_ << "#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl;
+ f_gen_ << "pub enum " << enum_name << " {" << endl;
+ indent_up();
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator constants_iter;
+ for (constants_iter = constants.begin(); constants_iter != constants.end(); ++constants_iter) {
+ t_enum_value* val = (*constants_iter);
+ render_rustdoc((t_doc*) val);
+ f_gen_
+ << indent()
+ << rust_enum_variant_name(val->get_name())
+ << " = "
+ << val->get_value()
+ << ","
+ << endl;
+ }
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_enum_impl(const string& enum_name) {
+ f_gen_ << "impl " << enum_name << " {" << endl;
+ indent_up();
+
+ f_gen_
+ << indent()
+ << "pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {"
+ << endl;
+ indent_up();
+ f_gen_ << indent() << "o_prot.write_i32(*self as i32)" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ f_gen_
+ << indent()
+ << "pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << enum_name << "> {"
+ << endl;
+ indent_up();
+
+ f_gen_ << indent() << "let enum_value = i_prot.read_i32()?;" << endl;
+ f_gen_ << indent() << enum_name << "::try_from(enum_value)";
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_enum_conversion(t_enum* tenum, const string& enum_name) {
+ f_gen_ << "impl TryFrom<i32> for " << enum_name << " {" << endl;
+ indent_up();
+
+ f_gen_ << indent() << "type Error = thrift::Error;";
+
+ f_gen_ << indent() << "fn try_from(i: i32) -> Result<Self, Self::Error> {" << endl;
+ indent_up();
+
+ f_gen_ << indent() << "match i {" << endl;
+ indent_up();
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator constants_iter;
+ for (constants_iter = constants.begin(); constants_iter != constants.end(); ++constants_iter) {
+ t_enum_value* val = (*constants_iter);
+ f_gen_
+ << indent()
+ << val->get_value()
+ << " => Ok(" << enum_name << "::" << rust_enum_variant_name(val->get_name()) << "),"
+ << endl;
+ }
+ f_gen_ << indent() << "_ => {" << endl;
+ indent_up();
+ render_thrift_error(
+ "Protocol",
+ "ProtocolError",
+ "ProtocolErrorKind::InvalidData",
+ "format!(\"cannot convert enum constant {} to " + enum_name + "\", i)"
+ );
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Structs, Unions and Exceptions
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::generate_xception(t_struct* txception) {
+ render_struct(rust_struct_name(txception), txception, t_rs_generator::T_EXCEPTION);
+}
+
+void t_rs_generator::generate_struct(t_struct* tstruct) {
+ if (tstruct->is_union()) {
+ render_union(tstruct);
+ } else if (tstruct->is_struct()) {
+ render_struct(rust_struct_name(tstruct), tstruct, t_rs_generator::T_REGULAR);
+ } else {
+ throw "cannot generate struct for exception";
+ }
+}
+
+void t_rs_generator::render_struct(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+) {
+ render_type_comment(struct_name);
+ render_struct_definition(struct_name, tstruct, struct_type);
+ render_struct_impl(struct_name, tstruct, struct_type);
+ if (struct_type == t_rs_generator::T_REGULAR || struct_type == t_rs_generator::T_EXCEPTION) {
+ render_struct_default_trait_impl(struct_name, tstruct);
+ }
+ if (struct_type == t_rs_generator::T_EXCEPTION) {
+ render_exception_struct_error_trait_impls(struct_name, tstruct);
+ }
+}
+
+void t_rs_generator::render_struct_definition(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+) {
+ render_rustdoc((t_doc*) tstruct);
+ f_gen_ << "#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl;
+ f_gen_ << visibility_qualifier(struct_type) << "struct " << struct_name << " {" << endl;
+
+ // render the members
+ vector<t_field*> members = tstruct->get_sorted_members();
+ if (!members.empty()) {
+ indent_up();
+
+ vector<t_field*>::iterator members_iter;
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = actual_field_req(member, struct_type);
+
+ string rust_type = to_rust_type(member->get_type());
+ rust_type = is_optional(member_req) ? "Option<" + rust_type + ">" : rust_type;
+
+ render_rustdoc((t_doc*) member);
+ f_gen_
+ << indent()
+ << visibility_qualifier(struct_type)
+ << rust_field_name(member) << ": " << rust_type << ","
+ << endl;
+ }
+
+ indent_down();
+ }
+
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_exception_struct_error_trait_impls(const string& struct_name, t_struct* tstruct) {
+ // error::Error trait
+ f_gen_ << "impl Error for " << struct_name << " {" << endl;
+ indent_up();
+ f_gen_ << indent() << "fn description(&self) -> &str {" << endl;
+ indent_up();
+ f_gen_ << indent() << "\"" << "remote service threw " << tstruct->get_name() << "\"" << endl; // use *original* name
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+
+ // convert::From trait
+ f_gen_ << "impl From<" << struct_name << "> for thrift::Error {" << endl;
+ indent_up();
+ f_gen_ << indent() << "fn from(e: " << struct_name << ") -> Self {" << endl;
+ indent_up();
+ f_gen_ << indent() << "thrift::Error::User(Box::new(e))" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+
+ // fmt::Display trait
+ f_gen_ << "impl Display for " << struct_name << " {" << endl;
+ indent_up();
+ f_gen_ << indent() << "fn fmt(&self, f: &mut Formatter) -> fmt::Result {" << endl;
+ indent_up();
+ f_gen_ << indent() << "self.description().fmt(f)" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_struct_default_trait_impl(const string& struct_name, t_struct* tstruct) {
+ bool has_required_field = false;
+
+ const vector<t_field*>& members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator members_iter;
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = *members_iter;
+ if (!is_optional(member->get_req())) {
+ has_required_field = true;
+ break;
+ }
+ }
+
+ if (has_required_field) {
+ return;
+ }
+
+ f_gen_ << "impl Default for " << struct_name << " {" << endl;
+ indent_up();
+ f_gen_ << indent() << "fn default() -> Self {" << endl;
+ indent_up();
+
+ if (members.empty()) {
+ f_gen_ << indent() << struct_name << "{}" << endl;
+ } else {
+ f_gen_ << indent() << struct_name << "{" << endl;
+ indent_up();
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field *member = (*members_iter);
+ string member_name(rust_field_name(member));
+ f_gen_ << indent() << member_name << ": " << opt_in_req_out_value(member->get_type()) << "," << endl;
+ }
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_struct_impl(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+) {
+ f_gen_ << "impl " << struct_name << " {" << endl;
+ indent_up();
+
+ if (struct_type == t_rs_generator::T_REGULAR || struct_type == t_rs_generator::T_EXCEPTION) {
+ render_struct_constructor(struct_name, tstruct, struct_type);
+ }
+
+ render_struct_sync_read(struct_name, tstruct, struct_type);
+ render_struct_sync_write(tstruct, struct_type);
+
+ if (struct_type == t_rs_generator::T_RESULT) {
+ render_result_struct_to_result_method(tstruct);
+ }
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_struct_constructor(
+ const string& struct_name,
+ t_struct* tstruct,
+ t_rs_generator::e_struct_type struct_type
+) {
+ const vector<t_field*>& members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator members_iter;
+
+ // build the convenience type parameters that allows us to pass unwrapped values to a constructor and
+ // have them automatically converted into Option<value>
+ bool first_arg = true;
+
+ ostringstream generic_type_parameters;
+ ostringstream generic_type_qualifiers;
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = actual_field_req(member, struct_type);
+
+ if (is_optional(member_req)) {
+ if (first_arg) {
+ first_arg = false;
+ } else {
+ generic_type_parameters << ", ";
+ generic_type_qualifiers << ", ";
+ }
+ generic_type_parameters << "F" << rust_safe_field_id(member->get_key());
+ generic_type_qualifiers << "F" << rust_safe_field_id(member->get_key()) << ": Into<Option<" << to_rust_type(member->get_type()) << ">>";
+ }
+ }
+
+ string type_parameter_string = generic_type_parameters.str();
+ if (type_parameter_string.length() != 0) {
+ type_parameter_string = "<" + type_parameter_string + ">";
+ }
+
+ string type_qualifier_string = generic_type_qualifiers.str();
+ if (type_qualifier_string.length() != 0) {
+ type_qualifier_string = "where " + type_qualifier_string + " ";
+ }
+
+ // now build the actual constructor arg list
+ // when we're building this list we have to use the type parameters in place of the actual type names
+ // if necessary
+ ostringstream args;
+ first_arg = true;
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = actual_field_req(member, struct_type);
+ string member_name(rust_field_name(member));
+
+ if (first_arg) {
+ first_arg = false;
+ } else {
+ args << ", ";
+ }
+
+ if (is_optional(member_req)) {
+ args << member_name << ": " << "F" << rust_safe_field_id(member->get_key());
+ } else {
+ args << member_name << ": " << to_rust_type(member->get_type());
+ }
+ }
+
+ string arg_string = args.str();
+
+ string visibility(visibility_qualifier(struct_type));
+ f_gen_
+ << indent()
+ << visibility
+ << "fn new"
+ << type_parameter_string
+ << "("
+ << arg_string
+ << ") -> "
+ << struct_name
+ << " "
+ << type_qualifier_string
+ << "{"
+ << endl;
+ indent_up();
+
+ if (members.size() == 0) {
+ f_gen_ << indent() << struct_name << " {}" << endl;
+ } else {
+ f_gen_ << indent() << struct_name << " {" << endl;
+ indent_up();
+
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = actual_field_req(member, struct_type);
+ string member_name(rust_field_name(member));
+
+ if (is_optional(member_req)) {
+ f_gen_ << indent() << member_name << ": " << member_name << ".into()," << endl;
+ } else {
+ f_gen_ << indent() << member_name << ": " << member_name << "," << endl;
+ }
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_result_struct_to_result_method(t_struct* tstruct) {
+ // we don't use the rust struct name in this method, just the service call name
+ string service_call_name = tstruct->get_name();
+
+ // check that we actually have a result
+ size_t index = service_call_name.find(RESULT_STRUCT_SUFFIX, 0);
+ if (index == std::string::npos) {
+ throw "result struct " + service_call_name + " missing result suffix";
+ } else {
+ service_call_name.replace(index, 6, "");
+ }
+
+ const vector<t_field*>& members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator members_iter;
+
+ // find out what the call's expected return type was
+ string rust_return_type = "()";
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ if (member->get_name() == SERVICE_RESULT_VARIABLE) { // don't have to check safe name here
+ rust_return_type = to_rust_type(member->get_type());
+ break;
+ }
+ }
+
+ // NOTE: ideally I would generate the branches and render them separately
+ // I tried this however, and the resulting code was harder to understand
+ // maintaining a rendered branch count (while a little ugly) got me the
+ // rendering I wanted with code that was reasonably understandable
+
+ f_gen_ << indent() << "fn ok_or(self) -> thrift::Result<" << rust_return_type << "> {" << endl;
+ indent_up();
+
+ int rendered_branch_count = 0;
+
+ // render the exception branches
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* tfield = (*members_iter);
+ if (tfield->get_name() != SERVICE_RESULT_VARIABLE) { // don't have to check safe name here
+ string field_name("self." + rust_field_name(tfield));
+ string branch_statement = rendered_branch_count == 0 ? "if" : "} else if";
+
+ f_gen_ << indent() << branch_statement << " " << field_name << ".is_some() {" << endl;
+ indent_up();
+ f_gen_ << indent() << "Err(thrift::Error::User(Box::new(" << field_name << ".unwrap())))" << endl;
+ indent_down();
+
+ rendered_branch_count++;
+ }
+ }
+
+ // render the return value branches
+ if (rust_return_type == "()") {
+ if (rendered_branch_count == 0) {
+ // we have the unit return and this service call has no user-defined
+ // exceptions. this means that we've a trivial return (happens with oneways)
+ f_gen_ << indent() << "Ok(())" << endl;
+ } else {
+ // we have the unit return, but there are user-defined exceptions
+ // if we've gotten this far then we have the default return (i.e. call successful)
+ f_gen_ << indent() << "} else {" << endl;
+ indent_up();
+ f_gen_ << indent() << "Ok(())" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+ } else {
+ string branch_statement = rendered_branch_count == 0 ? "if" : "} else if";
+ f_gen_ << indent() << branch_statement << " self." << SERVICE_RESULT_VARIABLE << ".is_some() {" << endl;
+ indent_up();
+ f_gen_ << indent() << "Ok(self." << SERVICE_RESULT_VARIABLE << ".unwrap())" << endl;
+ indent_down();
+ f_gen_ << indent() << "} else {" << endl;
+ indent_up();
+ // if we haven't found a valid return value *or* a user exception
+ // then we're in trouble; return a default error
+ render_thrift_error(
+ "Application",
+ "ApplicationError",
+ "ApplicationErrorKind::MissingResult",
+ "\"no result received for " + service_call_name + "\""
+ );
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_union(t_struct* tstruct) {
+ string union_name(rust_struct_name(tstruct));
+ render_type_comment(union_name);
+ render_union_definition(union_name, tstruct);
+ render_union_impl(union_name, tstruct);
+}
+
+void t_rs_generator::render_union_definition(const string& union_name, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_sorted_members();
+ if (members.empty()) {
+ throw "cannot generate rust enum with 0 members"; // may be valid thrift, but it's invalid rust
+ }
+
+ f_gen_ << "#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl;
+ f_gen_ << "pub enum " << union_name << " {" << endl;
+ indent_up();
+
+ vector<t_field*>::const_iterator member_iter;
+ for(member_iter = members.begin(); member_iter != members.end(); ++member_iter) {
+ t_field* tfield = (*member_iter);
+ f_gen_
+ << indent()
+ << rust_union_field_name(tfield)
+ << "(" << to_rust_type(tfield->get_type()) << "),"
+ << endl;
+ }
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_union_impl(const string& union_name, t_struct* tstruct) {
+ f_gen_ << "impl " << union_name << " {" << endl;
+ indent_up();
+
+ render_union_sync_read(union_name, tstruct);
+ render_union_sync_write(union_name, tstruct);
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Sync Struct Write
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::render_struct_sync_write(
+ t_struct *tstruct,
+ t_rs_generator::e_struct_type struct_type
+) {
+ f_gen_
+ << indent()
+ << visibility_qualifier(struct_type)
+ << "fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {"
+ << endl;
+ indent_up();
+
+ // write struct header to output protocol
+ // note: use the *original* struct name here
+ f_gen_ << indent() << "let struct_ident = TStructIdentifier::new(\"" + tstruct->get_name() + "\");" << endl;
+ f_gen_ << indent() << "o_prot.write_struct_begin(&struct_ident)?;" << endl;
+
+ // write struct members to output protocol
+ vector<t_field*> members = tstruct->get_sorted_members();
+ if (!members.empty()) {
+ vector<t_field*>::iterator members_iter;
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = actual_field_req(member, struct_type);
+ string member_var("self." + rust_field_name(member));
+ render_struct_field_sync_write(member_var, false, member, member_req);
+ }
+ }
+
+ // write struct footer to output protocol
+ f_gen_ << indent() << "o_prot.write_field_stop()?;" << endl;
+ f_gen_ << indent() << "o_prot.write_struct_end()" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_union_sync_write(const string &union_name, t_struct *tstruct) {
+ f_gen_
+ << indent()
+ << "pub fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {"
+ << endl;
+ indent_up();
+
+ // write struct header to output protocol
+ // note: use the *original* struct name here
+ f_gen_ << indent() << "let struct_ident = TStructIdentifier::new(\"" + tstruct->get_name() + "\");" << endl;
+ f_gen_ << indent() << "o_prot.write_struct_begin(&struct_ident)?;" << endl;
+
+ // write the enum field to the output protocol
+ vector<t_field*> members = tstruct->get_sorted_members();
+ if (!members.empty()) {
+ f_gen_ << indent() << "match *self {" << endl;
+ indent_up();
+ vector<t_field*>::iterator members_iter;
+ for(members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = t_field::T_REQUIRED;
+ t_type* ttype = member->get_type();
+ string match_var((ttype->is_base_type() && !ttype->is_string()) ? "f" : "ref f");
+ f_gen_
+ << indent()
+ << union_name << "::" << rust_union_field_name(member)
+ << "(" << match_var << ") => {"
+ << endl;
+ indent_up();
+ render_struct_field_sync_write("f", true, member, member_req);
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+ }
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+
+ // write struct footer to output protocol
+ f_gen_ << indent() << "o_prot.write_field_stop()?;" << endl;
+ f_gen_ << indent() << "o_prot.write_struct_end()" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_struct_field_sync_write(
+ const string &field_var,
+ bool field_var_is_ref,
+ t_field *tfield,
+ t_field::e_req req
+) {
+ t_type* field_type = tfield->get_type();
+ t_type* actual_type = get_true_type(field_type);
+
+ ostringstream field_stream;
+ field_stream
+ << "TFieldIdentifier::new("
+ << "\"" << tfield->get_name() << "\"" << ", " // note: use *original* name
+ << to_rust_field_type_enum(field_type) << ", "
+ << tfield->get_key() << ")";
+ string field_ident_string = field_stream.str();
+
+ if (is_optional(req)) {
+ string let_var((actual_type->is_base_type() && !actual_type->is_string()) ? "fld_var" : "ref fld_var");
+ f_gen_ << indent() << "if let Some(" << let_var << ") = " << field_var << " {" << endl;
+ indent_up();
+ f_gen_ << indent() << "o_prot.write_field_begin(&" << field_ident_string << ")?;" << endl;
+ render_type_sync_write("fld_var", true, field_type);
+ f_gen_ << indent() << "o_prot.write_field_end()?;" << endl;
+ f_gen_ << indent() << "()" << endl; // FIXME: remove this extraneous '()'
+ indent_down();
+ f_gen_ << indent() << "} else {" << endl; // FIXME: remove else branch
+ indent_up();
+ /* FIXME: rethink how I deal with OPT_IN_REQ_OUT
+ if (req == t_field::T_OPT_IN_REQ_OUT) {
+ f_gen_ << indent() << "let field_ident = " << field_ident_string << ";" << endl;
+ f_gen_ << indent() << "o_prot.write_field_begin(&field_ident)?;" << endl;
+ f_gen_ << indent() << "o_prot.write_field_end()?;" << endl;
+ }*/
+ f_gen_ << indent() << "()" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ } else {
+ f_gen_ << indent() << "o_prot.write_field_begin(&" << field_ident_string << ")?;" << endl;
+ render_type_sync_write(field_var, field_var_is_ref, tfield->get_type());
+ f_gen_ << indent() << "o_prot.write_field_end()?;" << endl;
+ }
+}
+
+void t_rs_generator::render_type_sync_write(const string &type_var, bool type_var_is_ref, t_type *ttype) {
+ if (ttype->is_base_type()) {
+ t_base_type* tbase_type = (t_base_type*)ttype;
+ switch (tbase_type->get_base()) {
+ case t_base_type::TYPE_VOID:
+ throw "cannot write field of type TYPE_VOID to output protocol";
+ case t_base_type::TYPE_STRING: {
+ string ref(type_var_is_ref ? "" : "&");
+ if (tbase_type->is_binary()) {
+ f_gen_ << indent() << "o_prot.write_bytes(" + ref + type_var + ")?;" << endl;
+ } else {
+ f_gen_ << indent() << "o_prot.write_string(" + ref + type_var + ")?;" << endl;
+ }
+ return;
+ }
+ case t_base_type::TYPE_BOOL:
+ f_gen_ << indent() << "o_prot.write_bool(" + type_var + ")?;" << endl;
+ return;
+ case t_base_type::TYPE_I8:
+ f_gen_ << indent() << "o_prot.write_i8(" + type_var + ")?;" << endl;
+ return;
+ case t_base_type::TYPE_I16:
+ f_gen_ << indent() << "o_prot.write_i16(" + type_var + ")?;" << endl;
+ return;
+ case t_base_type::TYPE_I32:
+ f_gen_ << indent() << "o_prot.write_i32(" + type_var + ")?;" << endl;
+ return;
+ case t_base_type::TYPE_I64:
+ f_gen_ << indent() << "o_prot.write_i64(" + type_var + ")?;" << endl;
+ return;
+ case t_base_type::TYPE_DOUBLE:
+ f_gen_ << indent() << "o_prot.write_double(" + type_var + ".into())?;" << endl;
+ return;
+ }
+ } else if (ttype->is_typedef()) {
+ t_typedef* ttypedef = (t_typedef*) ttype;
+ render_type_sync_write(type_var, type_var_is_ref, ttypedef->get_type());
+ return;
+ } else if (ttype->is_enum() || ttype->is_struct() || ttype->is_xception()) {
+ f_gen_ << indent() << type_var + ".write_to_out_protocol(o_prot)?;" << endl;
+ return;
+ } else if (ttype->is_map()) {
+ render_map_sync_write(type_var, type_var_is_ref, (t_map *) ttype);
+ return;
+ } else if (ttype->is_set()) {
+ render_set_sync_write(type_var, type_var_is_ref, (t_set *) ttype);
+ return;
+ } else if (ttype->is_list()) {
+ render_list_sync_write(type_var, type_var_is_ref, (t_list *) ttype);
+ return;
+ }
+
+ throw "cannot write unsupported type " + ttype->get_name();
+}
+
+void t_rs_generator::render_list_sync_write(const string &list_var, bool list_var_is_ref, t_list *tlist) {
+ t_type* elem_type = tlist->get_elem_type();
+
+ f_gen_
+ << indent()
+ << "o_prot.write_list_begin("
+ << "&TListIdentifier::new("
+ << to_rust_field_type_enum(elem_type) << ", "
+ << list_var << ".len() as i32" << ")"
+ << ")?;"
+ << endl;
+
+ string ref(list_var_is_ref ? "" : "&");
+ f_gen_ << indent() << "for e in " << ref << list_var << " {" << endl;
+ indent_up();
+ render_type_sync_write(string_container_write_variable(elem_type, "e"), true, elem_type);
+ f_gen_ << indent() << "o_prot.write_list_end()?;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_set_sync_write(const string &set_var, bool set_var_is_ref, t_set *tset) {
+ t_type* elem_type = tset->get_elem_type();
+
+ f_gen_
+ << indent()
+ << "o_prot.write_set_begin("
+ << "&TSetIdentifier::new("
+ << to_rust_field_type_enum(elem_type) << ", "
+ << set_var << ".len() as i32" << ")"
+ << ")?;"
+ << endl;
+
+ string ref(set_var_is_ref ? "" : "&");
+ f_gen_ << indent() << "for e in " << ref << set_var << " {" << endl;
+ indent_up();
+ render_type_sync_write(string_container_write_variable(elem_type, "e"), true, elem_type);
+ f_gen_ << indent() << "o_prot.write_set_end()?;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_map_sync_write(const string &map_var, bool map_var_is_ref, t_map *tmap) {
+ t_type* key_type = tmap->get_key_type();
+ t_type* val_type = tmap->get_val_type();
+
+ f_gen_
+ << indent()
+ << "o_prot.write_map_begin("
+ << "&TMapIdentifier::new("
+ << to_rust_field_type_enum(key_type) << ", "
+ << to_rust_field_type_enum(val_type) << ", "
+ << map_var << ".len() as i32)"
+ << ")?;"
+ << endl;
+
+ string ref(map_var_is_ref ? "" : "&");
+ f_gen_ << indent() << "for (k, v) in " << ref << map_var << " {" << endl;
+ indent_up();
+ render_type_sync_write(string_container_write_variable(key_type, "k"), true, key_type);
+ render_type_sync_write(string_container_write_variable(val_type, "v"), true, val_type);
+ f_gen_ << indent() << "o_prot.write_map_end()?;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+string t_rs_generator::string_container_write_variable(t_type* ttype, const string& base_var) {
+ bool type_needs_deref = needs_deref_on_container_write(ttype);
+ bool type_is_double = is_double(ttype);
+
+ string write_variable;
+
+ if (type_is_double && type_needs_deref) {
+ write_variable = "(*" + base_var + ")";
+ } else if (type_needs_deref) {
+ write_variable = "*" + base_var;
+ } else {
+ write_variable = base_var;
+ }
+
+ return write_variable;
+}
+
+bool t_rs_generator::needs_deref_on_container_write(t_type* ttype) {
+ ttype = get_true_type(ttype);
+ return ttype->is_base_type() && !ttype->is_string();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Sync Struct Read
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::render_struct_sync_read(
+ const string &struct_name,
+ t_struct *tstruct, t_rs_generator::e_struct_type struct_type
+) {
+ f_gen_
+ << indent()
+ << visibility_qualifier(struct_type)
+ << "fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << struct_name << "> {"
+ << endl;
+
+ indent_up();
+
+ f_gen_ << indent() << "i_prot.read_struct_begin()?;" << endl;
+
+ // create temporary variables: one for each field in the struct
+ const vector<t_field*> members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator members_iter;
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ t_field::e_req member_req = actual_field_req(member, struct_type);
+
+ f_gen_
+ << indent()
+ << "let mut " << struct_field_read_temp_variable(member)
+ << ": Option<" << to_rust_type(member->get_type()) << "> = ";
+ if (member_req == t_field::T_OPT_IN_REQ_OUT) {
+ f_gen_ << opt_in_req_out_value(member->get_type()) << ";";
+ } else {
+ f_gen_ << "None;";
+ }
+ f_gen_ << endl;
+ }
+
+ // now loop through the fields we've received
+ f_gen_ << indent() << "loop {" << endl; // start loop
+ indent_up();
+
+ // break out if you've found the Stop field
+ f_gen_ << indent() << "let field_ident = i_prot.read_field_begin()?;" << endl;
+ f_gen_ << indent() << "if field_ident.field_type == TType::Stop {" << endl;
+ indent_up();
+ f_gen_ << indent() << "break;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ // now read all the fields found
+ f_gen_ << indent() << "let field_id = field_id(&field_ident)?;" << endl;
+ f_gen_ << indent() << "match field_id {" << endl; // start match
+ indent_up();
+
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* tfield = (*members_iter);
+ f_gen_ << indent() << rust_safe_field_id(tfield->get_key()) << " => {" << endl;
+ indent_up();
+ render_type_sync_read("val", tfield->get_type());
+ f_gen_ << indent() << struct_field_read_temp_variable(tfield) << " = Some(val);" << endl;
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+ }
+
+ // default case (skip fields)
+ f_gen_ << indent() << "_ => {" << endl;
+ indent_up();
+ f_gen_ << indent() << "i_prot.skip(field_ident.field_type)?;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ indent_down();
+ f_gen_ << indent() << "};" << endl; // finish match
+ f_gen_ << indent() << "i_prot.read_field_end()?;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl; // finish loop
+ f_gen_ << indent() << "i_prot.read_struct_end()?;" << endl; // read message footer from the wire
+
+ // verify that all required fields exist
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* tfield = (*members_iter);
+ t_field::e_req req = actual_field_req(tfield, struct_type);
+ if (!is_optional(req)) {
+ f_gen_
+ << indent()
+ << "verify_required_field_exists("
+ << "\"" << struct_name << "." << rust_field_name(tfield) << "\""
+ << ", "
+ << "&" << struct_field_read_temp_variable(tfield)
+ << ")?;" << endl;
+ }
+ }
+
+ // construct the struct
+ if (members.size() == 0) {
+ f_gen_ << indent() << "let ret = " << struct_name << " {};" << endl;
+ } else {
+ f_gen_ << indent() << "let ret = " << struct_name << " {" << endl;
+ indent_up();
+
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* tfield = (*members_iter);
+ t_field::e_req req = actual_field_req(tfield, struct_type);
+ string field_name(rust_field_name(tfield));
+ string field_key = struct_field_read_temp_variable(tfield);
+ if (is_optional(req)) {
+ f_gen_ << indent() << field_name << ": " << field_key << "," << endl;
+ } else {
+ f_gen_
+ << indent()
+ << field_name
+ << ": "
+ << field_key
+ << ".expect(\"auto-generated code should have checked for presence of required fields\")"
+ << ","
+ << endl;
+ }
+ }
+
+ indent_down();
+ f_gen_ << indent() << "};" << endl;
+ }
+
+ // return the constructed value
+ f_gen_ << indent() << "Ok(ret)" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_union_sync_read(const string &union_name, t_struct *tstruct) {
+ f_gen_
+ << indent()
+ << "pub fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<" << union_name << "> {"
+ << endl;
+ indent_up();
+
+ // create temporary variables to hold the
+ // completed union as well as a count of fields read
+ f_gen_ << indent() << "let mut ret: Option<" << union_name << "> = None;" << endl;
+ f_gen_ << indent() << "let mut received_field_count = 0;" << endl;
+
+ // read the struct preamble
+ f_gen_ << indent() << "i_prot.read_struct_begin()?;" << endl;
+
+ // now loop through the fields we've received
+ f_gen_ << indent() << "loop {" << endl; // start loop
+ indent_up();
+
+ // break out if you've found the Stop field
+ f_gen_ << indent() << "let field_ident = i_prot.read_field_begin()?;" << endl;
+ f_gen_ << indent() << "if field_ident.field_type == TType::Stop {" << endl;
+ indent_up();
+ f_gen_ << indent() << "break;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ // now read all the fields found
+ f_gen_ << indent() << "let field_id = field_id(&field_ident)?;" << endl;
+ f_gen_ << indent() << "match field_id {" << endl; // start match
+ indent_up();
+
+ const vector<t_field*> members = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator members_iter;
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ f_gen_ << indent() << rust_safe_field_id(member->get_key()) << " => {" << endl;
+ indent_up();
+ render_type_sync_read("val", member->get_type());
+ f_gen_ << indent() << "if ret.is_none() {" << endl;
+ indent_up();
+ f_gen_
+ << indent()
+ << "ret = Some(" << union_name << "::" << rust_union_field_name(member) << "(val));"
+ << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << indent() << "received_field_count += 1;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+ }
+
+ // default case (skip fields)
+ f_gen_ << indent() << "_ => {" << endl;
+ indent_up();
+ f_gen_ << indent() << "i_prot.skip(field_ident.field_type)?;" << endl;
+ f_gen_ << indent() << "received_field_count += 1;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ indent_down();
+ f_gen_ << indent() << "};" << endl; // finish match
+ f_gen_ << indent() << "i_prot.read_field_end()?;" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl; // finish loop
+ f_gen_ << indent() << "i_prot.read_struct_end()?;" << endl; // finish reading message from wire
+
+ // return the value or an error
+ f_gen_ << indent() << "if received_field_count == 0 {" << endl;
+ indent_up();
+ render_thrift_error(
+ "Protocol",
+ "ProtocolError",
+ "ProtocolErrorKind::InvalidData",
+ "\"received empty union from remote " + union_name + "\""
+ );
+ indent_down();
+ f_gen_ << indent() << "} else if received_field_count > 1 {" << endl;
+ indent_up();
+ render_thrift_error(
+ "Protocol",
+ "ProtocolError",
+ "ProtocolErrorKind::InvalidData",
+ "\"received multiple fields for union from remote " + union_name + "\""
+ );
+ indent_down();
+ f_gen_ << indent() << "} else {" << endl;
+ indent_up();
+ f_gen_ << indent() << "Ok(ret.expect(\"return value should have been constructed\"))" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+// Construct the rust representation of all supported types from the wire.
+void t_rs_generator::render_type_sync_read(const string &type_var, t_type *ttype, bool is_boxed) {
+ if (ttype->is_base_type()) {
+ t_base_type* tbase_type = (t_base_type*)ttype;
+ switch (tbase_type->get_base()) {
+ case t_base_type::TYPE_VOID:
+ throw "cannot read field of type TYPE_VOID from input protocol";
+ case t_base_type::TYPE_STRING:
+ if (tbase_type->is_binary()) {
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_bytes()?;" << endl;
+ } else {
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_string()?;" << endl;
+ }
+ return;
+ case t_base_type::TYPE_BOOL:
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_bool()?;" << endl;
+ return;
+ case t_base_type::TYPE_I8:
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_i8()?;" << endl;
+ return;
+ case t_base_type::TYPE_I16:
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_i16()?;" << endl;
+ return;
+ case t_base_type::TYPE_I32:
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_i32()?;" << endl;
+ return;
+ case t_base_type::TYPE_I64:
+ f_gen_ << indent() << "let " << type_var << " = i_prot.read_i64()?;" << endl;
+ return;
+ case t_base_type::TYPE_DOUBLE:
+ f_gen_ << indent() << "let " << type_var << " = OrderedFloat::from(i_prot.read_double()?);" << endl;
+ return;
+ }
+ } else if (ttype->is_typedef()) {
+ // FIXME: not a fan of separate `is_boxed` parameter
+ // This is problematic because it's an optional parameter, and only comes
+ // into play once. The core issue is that I lose an important piece of type
+ // information (whether the type is a fwd ref) by unwrapping the typedef'd
+ // type and making the recursive call using it. I can't modify or wrap the
+ // generated string after the fact because it's written directly into the file,
+ // so I have to pass this parameter along. Going with this approach because it
+ // seems like the lowest-cost option to easily support recursive types.
+ t_typedef* ttypedef = (t_typedef*)ttype;
+ render_type_sync_read(type_var, ttypedef->get_type(), ttypedef->is_forward_typedef());
+ return;
+ } else if (ttype->is_enum() || ttype->is_struct() || ttype->is_xception()) {
+ string read_call(to_rust_type(ttype) + "::read_from_in_protocol(i_prot)?");
+ read_call = is_boxed ? "Box::new(" + read_call + ")" : read_call;
+ f_gen_
+ << indent()
+ << "let " << type_var << " = " << read_call << ";"
+ << endl;
+ return;
+ } else if (ttype->is_map()) {
+ render_map_sync_read((t_map *) ttype, type_var);
+ return;
+ } else if (ttype->is_set()) {
+ render_set_sync_read((t_set *) ttype, type_var);
+ return;
+ } else if (ttype->is_list()) {
+ render_list_sync_read((t_list *) ttype, type_var);
+ return;
+ }
+
+ throw "cannot read unsupported type " + ttype->get_name();
+}
+
+// Construct the rust representation of a list from the wire.
+void t_rs_generator::render_list_sync_read(t_list *tlist, const string &list_var) {
+ t_type* elem_type = tlist->get_elem_type();
+
+ f_gen_ << indent() << "let list_ident = i_prot.read_list_begin()?;" << endl;
+ f_gen_
+ << indent()
+ << "let mut " << list_var << ": " << to_rust_type((t_type*) tlist)
+ << " = Vec::with_capacity(list_ident.size as usize);"
+ << endl;
+ f_gen_ << indent() << "for _ in 0..list_ident.size {" << endl;
+
+ indent_up();
+
+ string list_elem_var = tmp("list_elem_");
+ render_type_sync_read(list_elem_var, elem_type);
+ f_gen_ << indent() << list_var << ".push(" << list_elem_var << ");" << endl;
+
+ indent_down();
+
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << indent() << "i_prot.read_list_end()?;" << endl;
+}
+
+// Construct the rust representation of a set from the wire.
+void t_rs_generator::render_set_sync_read(t_set *tset, const string &set_var) {
+ t_type* elem_type = tset->get_elem_type();
+
+ f_gen_ << indent() << "let set_ident = i_prot.read_set_begin()?;" << endl;
+ f_gen_
+ << indent()
+ << "let mut " << set_var << ": " << to_rust_type((t_type*) tset)
+ << " = BTreeSet::new();"
+ << endl;
+ f_gen_ << indent() << "for _ in 0..set_ident.size {" << endl;
+
+ indent_up();
+
+ string set_elem_var = tmp("set_elem_");
+ render_type_sync_read(set_elem_var, elem_type);
+ f_gen_ << indent() << set_var << ".insert(" << set_elem_var << ");" << endl;
+
+ indent_down();
+
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << indent() << "i_prot.read_set_end()?;" << endl;
+}
+
+// Construct the rust representation of a map from the wire.
+void t_rs_generator::render_map_sync_read(t_map *tmap, const string &map_var) {
+ t_type* key_type = tmap->get_key_type();
+ t_type* val_type = tmap->get_val_type();
+
+ f_gen_ << indent() << "let map_ident = i_prot.read_map_begin()?;" << endl;
+ f_gen_
+ << indent()
+ << "let mut " << map_var << ": " << to_rust_type((t_type*) tmap)
+ << " = BTreeMap::new();"
+ << endl;
+ f_gen_ << indent() << "for _ in 0..map_ident.size {" << endl;
+
+ indent_up();
+
+ string key_elem_var = tmp("map_key_");
+ render_type_sync_read(key_elem_var, key_type);
+ string val_elem_var = tmp("map_val_");
+ render_type_sync_read(val_elem_var, val_type);
+ f_gen_ << indent() << map_var << ".insert(" << key_elem_var << ", " << val_elem_var << ");" << endl;
+
+ indent_down();
+
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << indent() << "i_prot.read_map_end()?;" << endl;
+}
+
+string t_rs_generator::struct_field_read_temp_variable(t_field* tfield) {
+ std::ostringstream foss;
+ foss << "f_" << rust_safe_field_id(tfield->get_key());
+ return foss.str();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Sync Client
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::generate_service(t_service* tservice) {
+ render_sync_client(tservice);
+ render_sync_processor(tservice);
+ render_service_call_structs(tservice);
+}
+
+void t_rs_generator::render_service_call_structs(t_service* tservice) {
+ const std::vector<t_function*> functions = tservice->get_functions();
+ std::vector<t_function*>::const_iterator func_iter;
+
+ // thrift args for service calls are packed
+ // into a struct that's transmitted over the wire, so
+ // generate structs for those too
+ //
+ // thrift returns are *also* packed into a struct
+ // that's passed over the wire, so, generate the struct
+ // for that too. Note that this result struct *also*
+ // contains the exceptions as well
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* tfunc = (*func_iter);
+ render_service_call_args_struct(tfunc);
+ if (!tfunc->is_oneway()) {
+ render_service_call_result_value_struct(tfunc);
+ }
+ }
+}
+
+void t_rs_generator::render_sync_client(t_service* tservice) {
+ string client_impl_name(rust_sync_client_impl_name(tservice));
+
+ render_type_comment(tservice->get_name() + " service client"); // note: use *original* name
+ render_sync_client_trait(tservice);
+ render_sync_client_marker_trait(tservice);
+ render_sync_client_definition_and_impl(client_impl_name);
+ render_sync_client_tthriftclient_impl(client_impl_name);
+ render_sync_client_marker_trait_impls(tservice, client_impl_name); f_gen_ << endl;
+ render_sync_client_process_impl(tservice);
+}
+
+void t_rs_generator::render_sync_client_trait(t_service *tservice) {
+ string extension = "";
+ if (tservice->get_extends()) {
+ t_service* extends = tservice->get_extends();
+ extension = " : " + rust_namespace(extends) + rust_sync_client_trait_name(extends);
+ }
+
+ render_rustdoc((t_doc*) tservice);
+ f_gen_ << "pub trait " << rust_sync_client_trait_name(tservice) << extension << " {" << endl;
+ indent_up();
+
+ const std::vector<t_function*> functions = tservice->get_functions();
+ std::vector<t_function*>::const_iterator func_iter;
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* tfunc = (*func_iter);
+ string func_name = service_call_client_function_name(tfunc);
+ string func_args = rust_sync_service_call_declaration(tfunc, true);
+ string func_return = to_rust_type(tfunc->get_returntype());
+ render_rustdoc((t_doc*) tfunc);
+ f_gen_ << indent() << "fn " << func_name << func_args << " -> thrift::Result<" << func_return << ">;" << endl;
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_sync_client_marker_trait(t_service *tservice) {
+ f_gen_ << indent() << "pub trait " << rust_sync_client_marker_trait_name(tservice) << " {}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_sync_client_marker_trait_impls(t_service *tservice, const string &impl_struct_name) {
+ f_gen_
+ << indent()
+ << "impl "
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " "
+ << rust_namespace(tservice) << rust_sync_client_marker_trait_name(tservice)
+ << " for "
+ << impl_struct_name << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " "
+ << SYNC_CLIENT_GENERIC_BOUNDS
+ << " {}"
+ << endl;
+
+ t_service* extends = tservice->get_extends();
+ if (extends) {
+ render_sync_client_marker_trait_impls(extends, impl_struct_name);
+ }
+}
+
+void t_rs_generator::render_sync_client_definition_and_impl(const string& client_impl_name) {
+
+ // render the definition for the client struct
+ f_gen_
+ << "pub struct "
+ << client_impl_name
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " "
+ << SYNC_CLIENT_GENERIC_BOUNDS
+ << " {"
+ << endl;
+ indent_up();
+ f_gen_ << indent() << "_i_prot: IP," << endl;
+ f_gen_ << indent() << "_o_prot: OP," << endl;
+ f_gen_ << indent() << "_sequence_number: i32," << endl;
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+
+ // render the struct implementation
+ // this includes the new() function as well as the helper send/recv methods for each service call
+ f_gen_
+ << "impl "
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " "
+ << client_impl_name
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " "
+ << SYNC_CLIENT_GENERIC_BOUNDS
+ << " {"
+ << endl;
+ indent_up();
+ render_sync_client_lifecycle_functions(client_impl_name);
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_sync_client_lifecycle_functions(const string& client_struct) {
+ f_gen_
+ << indent()
+ << "pub fn new(input_protocol: IP, output_protocol: OP) -> "
+ << client_struct
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " {"
+ << endl;
+ indent_up();
+
+ f_gen_
+ << indent()
+ << client_struct
+ << " { _i_prot: input_protocol, _o_prot: output_protocol, _sequence_number: 0 }"
+ << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_sync_client_tthriftclient_impl(const string &client_impl_name) {
+ f_gen_
+ << indent()
+ << "impl "
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " TThriftClient for "
+ << client_impl_name
+ << SYNC_CLIENT_GENERIC_BOUND_VARS
+ << " "
+ << SYNC_CLIENT_GENERIC_BOUNDS
+ << " {" << endl;
+ indent_up();
+
+ f_gen_ << indent() << "fn i_prot_mut(&mut self) -> &mut dyn TInputProtocol { &mut self._i_prot }" << endl;
+ f_gen_ << indent() << "fn o_prot_mut(&mut self) -> &mut dyn TOutputProtocol { &mut self._o_prot }" << endl;
+ f_gen_ << indent() << "fn sequence_number(&self) -> i32 { self._sequence_number }" << endl;
+ f_gen_
+ << indent()
+ << "fn increment_sequence_number(&mut self) -> i32 { self._sequence_number += 1; self._sequence_number }"
+ << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_sync_client_process_impl(t_service* tservice) {
+ string marker_extension = "" + sync_client_marker_traits_for_extension(tservice);
+
+ f_gen_
+ << "impl <C: TThriftClient + " << rust_sync_client_marker_trait_name(tservice) << marker_extension << "> "
+ << rust_sync_client_trait_name(tservice)
+ << " for C {" << endl;
+ indent_up();
+
+ const std::vector<t_function*> functions = tservice->get_functions();
+ std::vector<t_function*>::const_iterator func_iter;
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* func = (*func_iter);
+ render_sync_send_recv_wrapper(func);
+ }
+
+ indent_down();
+ f_gen_ << "}" << endl;
+ f_gen_ << endl;
+}
+
+string t_rs_generator::sync_client_marker_traits_for_extension(t_service *tservice) {
+ string marker_extension;
+
+ t_service* extends = tservice->get_extends();
+ if (extends) {
+ marker_extension = " + " + rust_namespace(extends) + rust_sync_client_marker_trait_name(extends);
+ marker_extension = marker_extension + sync_client_marker_traits_for_extension(extends);
+ }
+
+ return marker_extension;
+}
+
+void t_rs_generator::render_sync_send_recv_wrapper(t_function* tfunc) {
+ string func_name = service_call_client_function_name(tfunc);
+ string func_decl_args = rust_sync_service_call_declaration(tfunc, true);
+ string func_call_args = rust_sync_service_call_invocation(tfunc);
+ string func_return = to_rust_type(tfunc->get_returntype());
+
+ f_gen_
+ << indent()
+ << "fn " << func_name << func_decl_args << " -> thrift::Result<" << func_return
+ << "> {"
+ << endl;
+ indent_up();
+
+ f_gen_ << indent() << "(" << endl;
+ indent_up();
+ render_sync_send(tfunc);
+ indent_down();
+ f_gen_ << indent() << ")?;" << endl;
+ if (tfunc->is_oneway()) {
+ f_gen_ << indent() << "Ok(())" << endl;
+ } else {
+ render_sync_recv(tfunc);
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_sync_send(t_function* tfunc) {
+ f_gen_ << indent() << "{" << endl;
+ indent_up();
+
+ // increment the sequence number and generate the call header
+ string message_type = tfunc->is_oneway() ? "TMessageType::OneWay" : "TMessageType::Call";
+ f_gen_ << indent() << "self.increment_sequence_number();" << endl;
+ f_gen_
+ << indent()
+ << "let message_ident = "
+ << "TMessageIdentifier::new(\"" << tfunc->get_name() << "\", " // note: use *original* name
+ << message_type << ", "
+ << "self.sequence_number());"
+ << endl;
+ // pack the arguments into the containing struct that we'll write out over the wire
+ // note that this struct is generated even if we have 0 args
+ ostringstream struct_definition;
+ vector<t_field*> members = tfunc->get_arglist()->get_sorted_members();
+ vector<t_field*>::iterator members_iter;
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* member = (*members_iter);
+ string member_name(rust_field_name(member));
+ struct_definition << member_name << ": " << member_name << ", ";
+ }
+ string struct_fields = struct_definition.str();
+ if (struct_fields.size() > 0) {
+ struct_fields = struct_fields.substr(0, struct_fields.size() - 2); // strip trailing comma
+ }
+ f_gen_
+ << indent()
+ << "let call_args = "
+ << service_call_args_struct_name(tfunc)
+ << " { "
+ << struct_fields
+ << " };"
+ << endl;
+ // write everything over the wire
+ f_gen_ << indent() << "self.o_prot_mut().write_message_begin(&message_ident)?;" << endl;
+ f_gen_ << indent() << "call_args.write_to_out_protocol(self.o_prot_mut())?;" << endl; // written even if we have 0 args
+ f_gen_ << indent() << "self.o_prot_mut().write_message_end()?;" << endl;
+ f_gen_ << indent() << "self.o_prot_mut().flush()" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_sync_recv(t_function* tfunc) {
+ f_gen_ << indent() << "{" << endl;
+ indent_up();
+
+ f_gen_ << indent() << "let message_ident = self.i_prot_mut().read_message_begin()?;" << endl;
+ f_gen_ << indent() << "verify_expected_sequence_number(self.sequence_number(), message_ident.sequence_number)?;" << endl;
+ f_gen_ << indent() << "verify_expected_service_call(\"" << tfunc->get_name() <<"\", &message_ident.name)?;" << endl; // note: use *original* name
+ // FIXME: replace with a "try" block
+ f_gen_ << indent() << "if message_ident.message_type == TMessageType::Exception {" << endl;
+ indent_up();
+ f_gen_ << indent() << "let remote_error = thrift::Error::read_application_error_from_in_protocol(self.i_prot_mut())?;" << endl;
+ f_gen_ << indent() << "self.i_prot_mut().read_message_end()?;" << endl;
+ f_gen_ << indent() << "return Err(thrift::Error::Application(remote_error))" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << indent() << "verify_expected_message_type(TMessageType::Reply, message_ident.message_type)?;" << endl;
+ f_gen_ << indent() << "let result = " << service_call_result_struct_name(tfunc) << "::read_from_in_protocol(self.i_prot_mut())?;" << endl;
+ f_gen_ << indent() << "self.i_prot_mut().read_message_end()?;" << endl;
+ f_gen_ << indent() << "result.ok_or()" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+string t_rs_generator::rust_sync_service_call_declaration(t_function* tfunc, bool self_is_mutable) {
+ ostringstream func_args;
+
+ if (self_is_mutable) {
+ func_args << "(&mut self";
+ } else {
+ func_args << "(&self";
+ }
+
+ if (has_args(tfunc)) {
+ func_args << ", "; // put comma after "self"
+ func_args << struct_to_declaration(tfunc->get_arglist(), T_ARGS);
+ }
+
+ func_args << ")";
+ return func_args.str();
+}
+
+string t_rs_generator::rust_sync_service_call_invocation(t_function* tfunc, const string& field_prefix) {
+ ostringstream func_args;
+ func_args << "(";
+
+ if (has_args(tfunc)) {
+ func_args << struct_to_invocation(tfunc->get_arglist(), field_prefix);
+ }
+
+ func_args << ")";
+ return func_args.str();
+}
+
+string t_rs_generator::struct_to_declaration(t_struct* tstruct, t_rs_generator::e_struct_type struct_type) {
+ ostringstream args;
+
+ bool first_arg = true;
+ std::vector<t_field*> fields = tstruct->get_sorted_members();
+ std::vector<t_field*>::iterator field_iter;
+ for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) {
+ t_field* tfield = (*field_iter);
+ t_field::e_req field_req = actual_field_req(tfield, struct_type);
+ string rust_type = to_rust_type(tfield->get_type());
+ rust_type = is_optional(field_req) ? "Option<" + rust_type + ">" : rust_type;
+
+ if (first_arg) {
+ first_arg = false;
+ } else {
+ args << ", ";
+ }
+
+ args << rust_field_name(tfield) << ": " << rust_type;
+ }
+
+ return args.str();
+}
+
+string t_rs_generator::struct_to_invocation(t_struct* tstruct, const string& field_prefix) {
+ ostringstream args;
+
+ bool first_arg = true;
+ std::vector<t_field*> fields = tstruct->get_sorted_members();
+ std::vector<t_field*>::iterator field_iter;
+ for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) {
+ t_field* tfield = (*field_iter);
+
+ if (first_arg) {
+ first_arg = false;
+ } else {
+ args << ", ";
+ }
+
+ args << field_prefix << rust_field_name(tfield);
+ }
+
+ return args.str();
+}
+
+void t_rs_generator::render_service_call_args_struct(t_function* tfunc) {
+ string args_struct_name(service_call_args_struct_name(tfunc));
+ render_struct(args_struct_name, tfunc->get_arglist(), t_rs_generator::T_ARGS);
+}
+
+void t_rs_generator::render_service_call_result_value_struct(t_function* tfunc) {
+ string result_struct_name = service_call_result_struct_name(tfunc);
+ t_struct result(program_, result_struct_name);
+
+ t_field return_value(tfunc->get_returntype(), SERVICE_RESULT_VARIABLE, 0);
+ return_value.set_req(t_field::T_OPTIONAL);
+ if (!tfunc->get_returntype()->is_void()) {
+ result.append(&return_value);
+ }
+
+ t_struct* exceptions = tfunc->get_xceptions();
+ const vector<t_field*>& exception_types = exceptions->get_members();
+ vector<t_field*>::const_iterator exception_iter;
+ for(exception_iter = exception_types.begin(); exception_iter != exception_types.end(); ++exception_iter) {
+ t_field* exception_type = *exception_iter;
+ exception_type->set_req(t_field::T_OPTIONAL);
+ result.append(exception_type);
+ }
+
+ render_struct(result_struct_name, &result, t_rs_generator::T_RESULT);
+}
+
+//-----------------------------------------------------------------------------
+//
+// Sync Processor
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::render_sync_processor(t_service *tservice) {
+ render_type_comment(tservice->get_name() + " service processor"); // note: use *original* name
+ render_sync_handler_trait(tservice);
+ render_sync_processor_definition_and_impl(tservice);
+}
+
+void t_rs_generator::render_sync_handler_trait(t_service *tservice) {
+ string extension = "";
+ if (tservice->get_extends() != NULL) {
+ t_service* extends = tservice->get_extends();
+ extension = " : " + rust_namespace(extends) + rust_sync_handler_trait_name(extends);
+ }
+
+ const std::vector<t_function*> functions = tservice->get_functions();
+ std::vector<t_function*>::const_iterator func_iter;
+
+ render_rustdoc((t_doc*) tservice);
+ f_gen_ << "pub trait " << rust_sync_handler_trait_name(tservice) << extension << " {" << endl;
+ indent_up();
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* tfunc = (*func_iter);
+ string func_name = service_call_handler_function_name(tfunc);
+ string func_args = rust_sync_service_call_declaration(tfunc, false);
+ string func_return = to_rust_type(tfunc->get_returntype());
+ render_rustdoc((t_doc*) tfunc);
+ f_gen_
+ << indent()
+ << "fn "
+ << func_name << func_args
+ << " -> thrift::Result<" << func_return << ">;"
+ << endl;
+ }
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_sync_processor_definition_and_impl(t_service *tservice) {
+ string service_processor_name = rust_sync_processor_name(tservice);
+ string handler_trait_name = rust_sync_handler_trait_name(tservice);
+
+ // struct
+ f_gen_
+ << indent()
+ << "pub struct " << service_processor_name
+ << "<H: " << handler_trait_name
+ << "> {"
+ << endl;
+ indent_up();
+ f_gen_ << indent() << "handler: H," << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+
+ // delegating impl
+ f_gen_
+ << indent()
+ << "impl <H: " << handler_trait_name << "> "
+ << service_processor_name
+ << "<H> {"
+ << endl;
+ indent_up();
+ f_gen_ << indent() << "pub fn new(handler: H) -> " << service_processor_name << "<H> {" << endl;
+ indent_up();
+ f_gen_ << indent() << service_processor_name << " {" << endl;
+ indent_up();
+ f_gen_ << indent() << "handler," << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ render_sync_process_delegation_functions(tservice);
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+
+ // actual impl
+ string service_actual_processor_name = rust_sync_processor_impl_name(tservice);
+ f_gen_ << indent() << "pub struct " << service_actual_processor_name << ";" << endl;
+ f_gen_ << endl;
+ f_gen_ << indent() << "impl " << service_actual_processor_name << " {" << endl;
+ indent_up();
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator func_iter;
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* tfunc = (*func_iter);
+ render_sync_process_function(tfunc, handler_trait_name);
+ }
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+
+ // processor impl
+ f_gen_
+ << indent()
+ << "impl <H: "
+ << handler_trait_name << "> TProcessor for "
+ << service_processor_name
+ << "<H> {"
+ << endl;
+ indent_up();
+
+ f_gen_
+ << indent()
+ << "fn process(&self, i_prot: &mut dyn TInputProtocol, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {"
+ << endl;
+ indent_up();
+
+ f_gen_ << indent() << "let message_ident = i_prot.read_message_begin()?;" << endl;
+
+ f_gen_ << indent() << "let res = match &*message_ident.name {" << endl; // [sigh] explicit deref coercion
+ indent_up();
+ render_process_match_statements(tservice);
+ f_gen_ << indent() << "method => {" << endl;
+ indent_up();
+ render_thrift_error(
+ "Application",
+ "ApplicationError",
+ "ApplicationErrorKind::UnknownMethod",
+ "format!(\"unknown method {}\", method)"
+ );
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ indent_down();
+ f_gen_ << indent() << "};" << endl;
+ f_gen_ << indent() << "thrift::server::handle_process_result(&message_ident, res, o_prot)" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ f_gen_ << endl;
+}
+
+void t_rs_generator::render_sync_process_delegation_functions(t_service *tservice) {
+ string actual_processor(rust_namespace(tservice) + rust_sync_processor_impl_name(tservice));
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator func_iter;
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* tfunc = (*func_iter);
+ string function_name("process_" + rust_snake_case(tfunc->get_name()));
+ f_gen_
+ << indent()
+ << "fn " << function_name
+ << "(&self, "
+ << "incoming_sequence_number: i32, "
+ << "i_prot: &mut dyn TInputProtocol, "
+ << "o_prot: &mut dyn TOutputProtocol) "
+ << "-> thrift::Result<()> {"
+ << endl;
+ indent_up();
+
+ f_gen_
+ << indent()
+ << actual_processor
+ << "::" << function_name
+ << "("
+ << "&self.handler, "
+ << "incoming_sequence_number, "
+ << "i_prot, "
+ << "o_prot"
+ << ")"
+ << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+ }
+
+ t_service* extends = tservice->get_extends();
+ if (extends) {
+ render_sync_process_delegation_functions(extends);
+ }
+}
+
+void t_rs_generator::render_process_match_statements(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator func_iter;
+ for(func_iter = functions.begin(); func_iter != functions.end(); ++func_iter) {
+ t_function* tfunc = (*func_iter);
+ f_gen_ << indent() << "\"" << tfunc->get_name() << "\"" << " => {" << endl; // note: use *original* name
+ indent_up();
+ f_gen_
+ << indent()
+ << "self.process_" << rust_snake_case(tfunc->get_name())
+ << "(message_ident.sequence_number, i_prot, o_prot)"
+ << endl;
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+ }
+
+ t_service* extends = tservice->get_extends();
+ if (extends) {
+ render_process_match_statements(extends);
+ }
+}
+
+void t_rs_generator::render_sync_process_function(t_function *tfunc, const string &handler_type) {
+ string sequence_number_param("incoming_sequence_number");
+ string output_protocol_param("o_prot");
+
+ if (tfunc->is_oneway()) {
+ sequence_number_param = "_";
+ output_protocol_param = "_";
+ }
+
+ f_gen_
+ << indent()
+ << "pub fn process_" << rust_snake_case(tfunc->get_name())
+ << "<H: " << handler_type << ">"
+ << "(handler: &H, "
+ << sequence_number_param << ": i32, "
+ << "i_prot: &mut dyn TInputProtocol, "
+ << output_protocol_param << ": &mut dyn TOutputProtocol) "
+ << "-> thrift::Result<()> {"
+ << endl;
+
+ indent_up();
+
+ // *always* read arguments from the input protocol
+ f_gen_
+ << indent()
+ << "let "
+ << (has_non_void_args(tfunc) ? "args" : "_")
+ << " = "
+ << service_call_args_struct_name(tfunc)
+ << "::read_from_in_protocol(i_prot)?;"
+ << endl;
+
+ f_gen_
+ << indent()
+ << "match handler."
+ << service_call_handler_function_name(tfunc)
+ << rust_sync_service_call_invocation(tfunc, "args.")
+ << " {"
+ << endl; // start match
+ indent_up();
+
+ // handler succeeded
+ string handler_return_variable = tfunc->is_oneway() || tfunc->get_returntype()->is_void() ? "_" : "handler_return";
+ f_gen_ << indent() << "Ok(" << handler_return_variable << ") => {" << endl;
+ indent_up();
+ render_sync_handler_succeeded(tfunc);
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+ // handler failed
+ f_gen_ << indent() << "Err(e) => {" << endl;
+ indent_up();
+ render_sync_handler_failed(tfunc);
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl; // end match
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl; // end function
+}
+
+void t_rs_generator::render_sync_handler_succeeded(t_function *tfunc) {
+ if (tfunc->is_oneway()) {
+ f_gen_ << indent() << "Ok(())" << endl;
+ } else {
+ f_gen_
+ << indent()
+ << "let message_ident = TMessageIdentifier::new("
+ << "\"" << tfunc->get_name() << "\", " // note: use *original* name
+ << "TMessageType::Reply, "
+ << "incoming_sequence_number);"
+ << endl;
+ f_gen_ << indent() << "o_prot.write_message_begin(&message_ident)?;" << endl;
+ f_gen_ << indent() << "let ret = " << handler_successful_return_struct(tfunc) <<";" << endl;
+ f_gen_ << indent() << "ret.write_to_out_protocol(o_prot)?;" << endl;
+ f_gen_ << indent() << "o_prot.write_message_end()?;" << endl;
+ f_gen_ << indent() << "o_prot.flush()" << endl;
+ }
+}
+
+void t_rs_generator::render_sync_handler_failed(t_function *tfunc) {
+ string err_var("e");
+
+ f_gen_ << indent() << "match " << err_var << " {" << endl;
+ indent_up();
+
+ // if there are any user-defined exceptions for this service call handle them first
+ if (tfunc->get_xceptions() != NULL && tfunc->get_xceptions()->get_sorted_members().size() > 0) {
+ string user_err_var("usr_err");
+ f_gen_ << indent() << "thrift::Error::User(" << user_err_var << ") => {" << endl;
+ indent_up();
+ render_sync_handler_failed_user_exception_branch(tfunc);
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+ }
+
+ // application error
+ string app_err_var("app_err");
+ f_gen_ << indent() << "thrift::Error::Application(" << app_err_var << ") => {" << endl;
+ indent_up();
+ render_sync_handler_failed_application_exception_branch(tfunc, app_err_var);
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ // default case
+ f_gen_ << indent() << "_ => {" << endl;
+ indent_up();
+ render_sync_handler_failed_default_exception_branch(tfunc);
+ indent_down();
+ f_gen_ << indent() << "}," << endl;
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_sync_handler_failed_user_exception_branch(t_function *tfunc) {
+ if (tfunc->get_xceptions() == NULL || tfunc->get_xceptions()->get_sorted_members().empty()) {
+ throw "cannot render user exception branches if no user exceptions defined";
+ }
+
+ const vector<t_field*> txceptions = tfunc->get_xceptions()->get_sorted_members();
+ vector<t_field*>::const_iterator xception_iter;
+ int branches_rendered = 0;
+
+ // run through all user-defined exceptions
+ for (xception_iter = txceptions.begin(); xception_iter != txceptions.end(); ++xception_iter) {
+ t_field* xception_field = (*xception_iter);
+
+ string if_statement(branches_rendered == 0 ? "if usr_err" : "} else if usr_err");
+ string exception_type(to_rust_type(xception_field->get_type()));
+ f_gen_ << indent() << if_statement << ".downcast_ref::<" << exception_type << ">().is_some() {" << endl;
+ indent_up();
+
+ f_gen_
+ << indent()
+ << "let err = usr_err.downcast::<" << exception_type << ">().expect(\"downcast already checked\");"
+ << endl;
+
+ // render the members of the return struct
+ ostringstream members;
+
+ bool has_result_variable = !(tfunc->is_oneway() || tfunc->get_returntype()->is_void());
+ if (has_result_variable) {
+ members << SERVICE_RESULT_VARIABLE << ": None, ";
+ }
+
+ vector<t_field*>::const_iterator xception_members_iter;
+ for(xception_members_iter = txceptions.begin(); xception_members_iter != txceptions.end(); ++xception_members_iter) {
+ t_field* member = (*xception_members_iter);
+ string member_name(rust_field_name(member));
+ if (member == xception_field) {
+ members << member_name << ": Some(*err), ";
+ } else {
+ members << member_name << ": None, ";
+ }
+ }
+
+ string member_string = members.str();
+ member_string.replace(member_string.size() - 2, 2, " "); // trim trailing comma
+
+ // now write out the return struct
+ f_gen_
+ << indent()
+ << "let ret_err = "
+ << service_call_result_struct_name(tfunc)
+ << "{ " << member_string << "};"
+ << endl;
+
+ f_gen_
+ << indent()
+ << "let message_ident = "
+ << "TMessageIdentifier::new("
+ << "\"" << tfunc->get_name() << "\", " // note: use *original* name
+ << "TMessageType::Reply, "
+ << "incoming_sequence_number);"
+ << endl;
+ f_gen_ << indent() << "o_prot.write_message_begin(&message_ident)?;" << endl;
+ f_gen_ << indent() << "ret_err.write_to_out_protocol(o_prot)?;" << endl;
+ f_gen_ << indent() << "o_prot.write_message_end()?;" << endl;
+ f_gen_ << indent() << "o_prot.flush()" << endl;
+
+ indent_down();
+
+ branches_rendered++;
+ }
+
+ // the catch all, if somehow it was a user exception that we don't support
+ f_gen_ << indent() << "} else {" << endl;
+ indent_up();
+
+ // FIXME: same as default block below
+
+ f_gen_ << indent() << "let ret_err = {" << endl;
+ indent_up();
+ render_thrift_error_struct("ApplicationError", "ApplicationErrorKind::Unknown", "usr_err.description()");
+ indent_down();
+ f_gen_ << indent() << "};" << endl;
+ render_sync_handler_send_exception_response(tfunc, "ret_err");
+
+ indent_down();
+ f_gen_ << indent() << "}" << endl;
+}
+
+void t_rs_generator::render_sync_handler_failed_application_exception_branch(
+ t_function *tfunc,
+ const string &app_err_var
+) {
+ if (tfunc->is_oneway()) {
+ f_gen_ << indent() << "Err(thrift::Error::Application(" << app_err_var << "))" << endl;
+ } else {
+ render_sync_handler_send_exception_response(tfunc, app_err_var);
+ }
+}
+
+void t_rs_generator::render_sync_handler_failed_default_exception_branch(t_function *tfunc) {
+ f_gen_ << indent() << "let ret_err = {" << endl;
+ indent_up();
+ render_thrift_error_struct("ApplicationError", "ApplicationErrorKind::Unknown", "e.description()");
+ indent_down();
+ f_gen_ << indent() << "};" << endl;
+ if (tfunc->is_oneway()) {
+ f_gen_ << indent() << "Err(thrift::Error::Application(ret_err))" << endl;
+ } else {
+ render_sync_handler_send_exception_response(tfunc, "ret_err");
+ }
+}
+
+void t_rs_generator::render_sync_handler_send_exception_response(t_function *tfunc, const string &err_var) {
+ f_gen_
+ << indent()
+ << "let message_ident = TMessageIdentifier::new("
+ << "\"" << tfunc->get_name() << "\", " // note: use *original* name
+ << "TMessageType::Exception, "
+ << "incoming_sequence_number);"
+ << endl;
+ f_gen_ << indent() << "o_prot.write_message_begin(&message_ident)?;" << endl;
+ f_gen_ << indent() << "thrift::Error::write_application_error_to_out_protocol(&" << err_var << ", o_prot)?;" << endl;
+ f_gen_ << indent() << "o_prot.write_message_end()?;" << endl;
+ f_gen_ << indent() << "o_prot.flush()" << endl;
+}
+
+string t_rs_generator::handler_successful_return_struct(t_function* tfunc) {
+ int member_count = 0;
+ ostringstream return_struct;
+
+ return_struct << service_call_result_struct_name(tfunc) << " { ";
+
+ // actual return
+ if (!tfunc->get_returntype()->is_void()) {
+ return_struct << "result_value: Some(handler_return)";
+ member_count++;
+ }
+
+ // any user-defined exceptions
+ if (tfunc->get_xceptions() != NULL) {
+ t_struct* txceptions = tfunc->get_xceptions();
+ const vector<t_field*> members = txceptions->get_sorted_members();
+ vector<t_field*>::const_iterator members_iter;
+ for (members_iter = members.begin(); members_iter != members.end(); ++members_iter) {
+ t_field* xception_field = (*members_iter);
+ if (member_count > 0) { return_struct << ", "; }
+ return_struct << rust_field_name(xception_field) << ": None";
+ member_count++;
+ }
+ }
+
+ return_struct << " }";
+
+ return return_struct.str();
+}
+
+//-----------------------------------------------------------------------------
+//
+// Utility
+//
+//-----------------------------------------------------------------------------
+
+void t_rs_generator::render_type_comment(const string& type_name) {
+ f_gen_ << "//" << endl;
+ f_gen_ << "// " << type_name << endl;
+ f_gen_ << "//" << endl;
+ f_gen_ << endl;
+}
+
+// NOTE: do *not* put in an extra newline after doc is generated.
+// This is because rust docs have to abut the line they're documenting.
+void t_rs_generator::render_rustdoc(t_doc* tdoc) {
+ if (!tdoc->has_doc()) {
+ return;
+ }
+
+ generate_docstring_comment(f_gen_, "", "/// ", tdoc->get_doc(), "");
+}
+
+void t_rs_generator::render_thrift_error(
+ const string& error_kind,
+ const string& error_struct,
+ const string& sub_error_kind,
+ const string& error_message
+) {
+ f_gen_ << indent() << "Err(" << endl;
+ indent_up();
+ f_gen_ << indent() << "thrift::Error::" << error_kind << "(" << endl;
+ indent_up();
+ render_thrift_error_struct(error_struct, sub_error_kind, error_message);
+ indent_down();
+ f_gen_ << indent() << ")" << endl;
+ indent_down();
+ f_gen_ << indent() << ")" << endl;
+}
+
+void t_rs_generator::render_thrift_error_struct(
+ const string& error_struct,
+ const string& sub_error_kind,
+ const string& error_message
+) {
+ f_gen_ << indent() << error_struct << "::new(" << endl;
+ indent_up();
+ f_gen_ << indent() << sub_error_kind << "," << endl;
+ f_gen_ << indent() << error_message << endl;
+ indent_down();
+ f_gen_ << indent() << ")" << endl;
+}
+
+bool t_rs_generator::is_double(t_type* ttype) {
+ ttype = get_true_type(ttype);
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ if (tbase == t_base_type::TYPE_DOUBLE) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+string t_rs_generator::to_rust_type(t_type* ttype, bool ordered_float) {
+ // ttype = get_true_type(ttype); <-- recurses through as many typedef layers as necessary
+ if (ttype->is_base_type()) {
+ t_base_type* tbase_type = ((t_base_type*)ttype);
+ switch (tbase_type->get_base()) {
+ case t_base_type::TYPE_VOID:
+ return "()";
+ case t_base_type::TYPE_STRING:
+ if (tbase_type->is_binary()) {
+ return "Vec<u8>";
+ } else {
+ return "String";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "bool";
+ case t_base_type::TYPE_I8:
+ return "i8";
+ case t_base_type::TYPE_I16:
+ return "i16";
+ case t_base_type::TYPE_I32:
+ return "i32";
+ case t_base_type::TYPE_I64:
+ return "i64";
+ case t_base_type::TYPE_DOUBLE:
+ if (ordered_float) {
+ return "OrderedFloat<f64>";
+ } else {
+ return "f64";
+ }
+ }
+ } else if (ttype->is_typedef()) {
+ t_typedef* ttypedef = (t_typedef*)ttype;
+ string rust_type = rust_namespace(ttype) + ttypedef->get_symbolic();
+ rust_type = ttypedef->is_forward_typedef() ? "Box<" + rust_type + ">" : rust_type;
+ return rust_type;
+ } else if (ttype->is_enum()) {
+ return rust_namespace(ttype) + rust_camel_case(ttype->get_name());
+ } else if (ttype->is_struct() || ttype->is_xception()) {
+ return rust_namespace(ttype) + rust_camel_case(ttype->get_name());
+ } else if (ttype->is_map()) {
+ t_map* tmap = (t_map*)ttype;
+ return "BTreeMap<" + to_rust_type(tmap->get_key_type()) + ", " + to_rust_type(tmap->get_val_type()) + ">";
+ } else if (ttype->is_set()) {
+ t_set* tset = (t_set*)ttype;
+ return "BTreeSet<" + to_rust_type(tset->get_elem_type()) + ">";
+ } else if (ttype->is_list()) {
+ t_list* tlist = (t_list*)ttype;
+ return "Vec<" + to_rust_type(tlist->get_elem_type()) + ">";
+ }
+
+ throw "cannot find rust type for " + ttype->get_name();
+}
+
+string t_rs_generator::to_rust_const_type(t_type* ttype, bool ordered_float) {
+ if (ttype->is_base_type()) {
+ t_base_type* tbase_type = ((t_base_type*)ttype);
+ if (tbase_type->get_base() == t_base_type::TYPE_STRING) {
+ if (tbase_type->is_binary()) {
+ return "&[u8]";
+ } else {
+ return "&str";
+ }
+ }
+ }
+
+ return to_rust_type(ttype, ordered_float);
+}
+
+string t_rs_generator::to_rust_field_type_enum(t_type* ttype) {
+ ttype = get_true_type(ttype);
+ if (ttype->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "will not generate protocol::TType for TYPE_VOID";
+ case t_base_type::TYPE_STRING: // both strings and binary are actually encoded as TType::String
+ return "TType::String";
+ case t_base_type::TYPE_BOOL:
+ return "TType::Bool";
+ case t_base_type::TYPE_I8:
+ return "TType::I08";
+ case t_base_type::TYPE_I16:
+ return "TType::I16";
+ case t_base_type::TYPE_I32:
+ return "TType::I32";
+ case t_base_type::TYPE_I64:
+ return "TType::I64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType::Double";
+ }
+ } else if (ttype->is_enum()) {
+ return "TType::I32";
+ } else if (ttype->is_struct() || ttype->is_xception()) {
+ return "TType::Struct";
+ } else if (ttype->is_map()) {
+ return "TType::Map";
+ } else if (ttype->is_set()) {
+ return "TType::Set";
+ } else if (ttype->is_list()) {
+ return "TType::List";
+ }
+
+ throw "cannot find TType for " + ttype->get_name();
+}
+
+string t_rs_generator::opt_in_req_out_value(t_type* ttype) {
+ ttype = get_true_type(ttype);
+ if (ttype->is_base_type()) {
+ t_base_type* tbase_type = ((t_base_type*)ttype);
+ switch (tbase_type->get_base()) {
+ case t_base_type::TYPE_VOID:
+ throw "cannot generate OPT_IN_REQ_OUT value for void";
+ case t_base_type::TYPE_STRING:
+ if (tbase_type->is_binary()) {
+ return "Some(Vec::new())";
+ } else {
+ return "Some(\"\".to_owned())";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "Some(false)";
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "Some(0)";
+ case t_base_type::TYPE_DOUBLE:
+ return "Some(OrderedFloat::from(0.0))";
+ }
+
+ } else if (ttype->is_enum() || ttype->is_struct() || ttype->is_xception()) {
+ return "None";
+ } else if (ttype->is_list()) {
+ return "Some(Vec::new())";
+ } else if (ttype->is_set()) {
+ return "Some(BTreeSet::new())";
+ } else if (ttype->is_map()) {
+ return "Some(BTreeMap::new())";
+ }
+
+ throw "cannot generate opt-in-req-out value for type " + ttype->get_name();
+}
+
+bool t_rs_generator::can_generate_simple_const(t_type* ttype) {
+ t_type* actual_type = get_true_type(ttype);
+ if (actual_type->is_base_type()) {
+ t_base_type* tbase_type = (t_base_type*)actual_type;
+ return !(tbase_type->get_base() == t_base_type::TYPE_DOUBLE);
+ } else {
+ return false;
+ }
+}
+
+bool t_rs_generator::can_generate_const_holder(t_type* ttype) {
+ t_type* actual_type = get_true_type(ttype);
+ return !can_generate_simple_const(actual_type) && !actual_type->is_service();
+}
+
+bool t_rs_generator::is_void(t_type* ttype) {
+ return ttype->is_base_type() && ((t_base_type*)ttype)->get_base() == t_base_type::TYPE_VOID;
+}
+
+bool t_rs_generator::is_optional(t_field::e_req req) {
+ return req == t_field::T_OPTIONAL || req == t_field::T_OPT_IN_REQ_OUT;
+}
+
+t_field::e_req t_rs_generator::actual_field_req(t_field* tfield, t_rs_generator::e_struct_type struct_type) {
+ return struct_type == t_rs_generator::T_ARGS ? t_field::T_REQUIRED : tfield->get_req();
+}
+
+bool t_rs_generator::has_args(t_function* tfunc) {
+ return tfunc->get_arglist() != NULL && !tfunc->get_arglist()->get_sorted_members().empty();
+}
+
+bool t_rs_generator::has_non_void_args(t_function* tfunc) {
+ bool has_non_void_args = false;
+
+ const vector<t_field*> args = tfunc->get_arglist()->get_sorted_members();
+ vector<t_field*>::const_iterator args_iter;
+ for (args_iter = args.begin(); args_iter != args.end(); ++args_iter) {
+ t_field* tfield = (*args_iter);
+ if (!tfield->get_type()->is_void()) {
+ has_non_void_args = true;
+ break;
+ }
+ }
+
+ return has_non_void_args;
+}
+
+string t_rs_generator::visibility_qualifier(t_rs_generator::e_struct_type struct_type) {
+ switch(struct_type) {
+ case t_rs_generator::T_ARGS:
+ case t_rs_generator::T_RESULT:
+ return "";
+ default:
+ return "pub ";
+ }
+}
+
+string t_rs_generator::rust_namespace(t_service* tservice) {
+ if (tservice->get_program()->get_name() != get_program()->get_name()) {
+ return rust_snake_case(tservice->get_program()->get_name()) + "::";
+ } else {
+ return "";
+ }
+}
+
+string t_rs_generator::rust_namespace(t_type* ttype) {
+ if (ttype->get_program()->get_name() != get_program()->get_name()) {
+ return rust_snake_case(ttype->get_program()->get_name()) + "::";
+ } else {
+ return "";
+ }
+}
+
+bool t_rs_generator::is_reserved(const string& name) {
+ return RUST_RESERVED_WORDS_SET.find(name) != RUST_RESERVED_WORDS_SET.end();
+}
+
+string t_rs_generator::rust_struct_name(t_struct* tstruct) {
+ string base_struct_name(rust_camel_case(tstruct->get_name()));
+ return rust_safe_name(base_struct_name);
+}
+
+string t_rs_generator::rust_field_name(t_field* tfield) {
+ string base_field_name(rust_snake_case(tfield->get_name()));
+ return rust_safe_name(base_field_name);
+}
+
+string t_rs_generator::rust_union_field_name(t_field* tfield) {
+ string base_field_name(rust_camel_case(tfield->get_name()));
+ return rust_safe_name(base_field_name);
+}
+
+string t_rs_generator::rust_safe_name(const string& name) {
+ if (is_reserved(name)) {
+ return name + "_";
+ } else {
+ return name;
+ }
+}
+
+string t_rs_generator::service_call_client_function_name(t_function* tfunc) {
+ return rust_snake_case(tfunc->get_name());
+}
+
+string t_rs_generator::service_call_handler_function_name(t_function* tfunc) {
+ return "handle_" + rust_snake_case(tfunc->get_name());
+}
+
+string t_rs_generator::service_call_args_struct_name(t_function* tfunc) {
+ // Thrift automatically appends `Args` to the arglist name. No need to do it here.
+ return rust_camel_case(service_name_) + rust_camel_case(tfunc->get_arglist()->get_name());
+}
+
+string t_rs_generator::service_call_result_struct_name(t_function* tfunc) {
+ return rust_camel_case(service_name_) + rust_camel_case(tfunc->get_name()) + RESULT_STRUCT_SUFFIX;
+}
+
+string t_rs_generator::rust_sync_client_marker_trait_name(t_service* tservice) {
+ return "T" + rust_camel_case(tservice->get_name()) + "SyncClientMarker";
+}
+
+string t_rs_generator::rust_sync_client_trait_name(t_service* tservice) {
+ return "T" + rust_camel_case(tservice->get_name()) + "SyncClient";
+}
+
+string t_rs_generator::rust_sync_client_impl_name(t_service* tservice) {
+ return rust_camel_case(tservice->get_name()) + "SyncClient";
+}
+
+string t_rs_generator::rust_sync_handler_trait_name(t_service* tservice) {
+ return rust_camel_case(tservice->get_name()) + "SyncHandler";
+}
+
+string t_rs_generator::rust_sync_processor_name(t_service* tservice) {
+ return rust_camel_case(tservice->get_name()) + "SyncProcessor";
+}
+
+string t_rs_generator::rust_sync_processor_impl_name(t_service *tservice) {
+ return "T" + rust_camel_case(tservice->get_name()) + "ProcessFunctions";
+}
+
+string t_rs_generator::rust_enum_variant_name(const string &name) {
+ bool all_uppercase = true;
+
+ for (char i : name) {
+ if (isalnum(i) && islower(i)) {
+ all_uppercase = false;
+ break;
+ }
+ }
+
+ if (all_uppercase) {
+ return capitalize(camelcase(lowercase(name)));
+ } else {
+ return capitalize(camelcase(name));
+ }
+}
+
+string t_rs_generator::rust_upper_case(const string& name) {
+ string str(uppercase(underscore(name)));
+ string_replace(str, "__", "_");
+ return str;
+}
+
+string t_rs_generator::rust_snake_case(const string& name) {
+ string str(decapitalize(underscore(name)));
+ string_replace(str, "__", "_");
+ return str;
+}
+
+string t_rs_generator::rust_camel_case(const string& name) {
+ string str(capitalize(camelcase(name)));
+ string_replace(str, "_", "");
+ return str;
+}
+
+string t_rs_generator::rust_safe_field_id(int32_t id) {
+ string id_str = std::to_string(abs(id));
+ if (id >= 0) {
+ return id_str;
+ } else {
+ string str("neg");
+ str += id_str;
+ return str;
+ }
+}
+
+void t_rs_generator::string_replace(string& target, const string& search_string, const string& replace_string) {
+ if (target.empty()) {
+ return;
+ }
+
+ size_t match_len = search_string.length();
+ size_t replace_len = replace_string.length();
+
+ size_t search_idx = 0;
+ size_t match_idx;
+ while ((match_idx = target.find(search_string, search_idx)) != string::npos) {
+ target.replace(match_idx, match_len, replace_string);
+ search_idx = match_idx + replace_len;
+ }
+}
+
+THRIFT_REGISTER_GENERATOR(
+ rs,
+ "Rust",
+ "\n") // no Rust-generator-specific options
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc
new file mode 100644
index 000000000..bc95feb66
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc
@@ -0,0 +1,1056 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <stdlib.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/version.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Smalltalk code generator.
+ *
+ */
+class t_st_generator : public t_oop_generator {
+public:
+ t_st_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ temporary_var = 0;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option st:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-st";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_const(t_const* tconst) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+ void generate_class_side_definition();
+ void generate_force_consts();
+
+ std::string render_const_value(t_type* type, t_const_value* value);
+
+ /**
+ * Struct generation code
+ */
+
+ void generate_st_struct(std::ostream& out, t_struct* tstruct, bool is_exception);
+ void generate_accessors(std::ostream& out, t_struct* tstruct);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_service_client(t_service* tservice);
+
+ void generate_send_method(t_function* tfunction);
+ void generate_recv_method(t_function* tfunction);
+
+ std::string map_reader(t_map* tmap);
+ std::string list_reader(t_list* tlist);
+ std::string set_reader(t_set* tset);
+ std::string struct_reader(t_struct* tstruct, std::string clsName);
+
+ std::string map_writer(t_map* tmap, std::string name);
+ std::string list_writer(t_list* tlist, std::string name);
+ std::string set_writer(t_set* tset, std::string name);
+ std::string struct_writer(t_struct* tstruct, std::string fname);
+
+ std::string write_val(t_type* t, std::string fname);
+ std::string read_val(t_type* t);
+
+ /**
+ * Helper rendering functions
+ */
+
+ std::string st_autogen_comment();
+
+ void st_class_def(std::ostream& out, std::string name);
+ void st_method(std::ostream& out, std::string cls, std::string name);
+ void st_method(std::ostream& out, std::string cls, std::string name, std::string category);
+ void st_close_method(std::ostream& out);
+ void st_class_method(std::ostream& out, std::string cls, std::string name);
+ void st_class_method(std::ostream& out, std::string cls, std::string name, std::string category);
+ void st_setter(std::ostream& out, std::string cls, std::string name, std::string type);
+ void st_getter(std::ostream& out, std::string cls, std::string name);
+ void st_accessors(std::ostream& out, std::string cls, std::string name, std::string type);
+
+ std::string class_name();
+ static bool is_valid_namespace(const std::string& sub_namespace);
+ std::string client_class_name();
+ std::string prefix(std::string name);
+ std::string declare_field(t_field* tfield);
+ std::string type_name(t_type* ttype);
+
+ std::string function_signature(t_function* tfunction);
+ std::string argument_list(t_struct* tstruct);
+ std::string function_types_comment(t_function* fn);
+
+ std::string type_to_enum(t_type* ttype);
+ std::string a_type(t_type* type);
+ bool is_vowel(char c);
+ std::string temp_name();
+ std::string generated_category();
+
+private:
+ /**
+ * File streams
+ */
+ int temporary_var;
+ ofstream_with_content_based_conditional_update f_;
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ *
+ * @param tprogram The program to generate
+ */
+void t_st_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ temporary_var = 0;
+
+ // Make output file
+ string f_name = get_out_dir() + "/" + program_name_ + ".st";
+ f_.open(f_name.c_str());
+
+ // Print header
+ f_ << st_autogen_comment() << endl;
+
+ st_class_def(f_, program_name_);
+ generate_class_side_definition();
+
+ // Generate enums
+ vector<t_enum*> enums = program_->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ generate_enum(*en_iter);
+ }
+}
+
+string t_st_generator::class_name() {
+ return capitalize(program_name_);
+}
+
+bool t_st_generator::is_valid_namespace(const std::string& sub_namespace) {
+ return sub_namespace == "prefix" || sub_namespace == "category";
+}
+
+string t_st_generator::prefix(string class_name) {
+ string prefix = program_->get_namespace("smalltalk.prefix");
+ string name = capitalize(class_name);
+ name = prefix.empty() ? name : (prefix + name);
+ return name;
+}
+
+string t_st_generator::client_class_name() {
+ return capitalize(service_name_) + "Client";
+}
+
+/**
+ * Autogen'd comment
+ */
+string t_st_generator::st_autogen_comment() {
+ return std::string("'") + "Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "\n"
+ + "DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "'!\n";
+}
+
+void t_st_generator::generate_force_consts() {
+ f_ << prefix(class_name()) << " enums keysAndValuesDo: [:k :v | " << prefix(class_name())
+ << " enums at: k put: v value].!" << endl;
+
+ f_ << prefix(class_name()) << " constants keysAndValuesDo: [:k :v | " << prefix(class_name())
+ << " constants at: k put: v value].!" << endl;
+}
+
+void t_st_generator::close_generator() {
+ generate_force_consts();
+ f_.close();
+}
+
+string t_st_generator::generated_category() {
+ string cat = program_->get_namespace("smalltalk.category");
+ // For compatibility with the Thrift grammar, the category must
+ // be punctuated by dots. Replaces them with dashes here.
+ for (char & iter : cat) {
+ if (iter == '.') {
+ iter = '-';
+ }
+ }
+ return cat.size() ? cat : "Generated-" + class_name();
+}
+
+/**
+ * Generates a typedef. This is not done in Smalltalk, types are all implicit.
+ *
+ * @param ttypedef The type definition
+ */
+void t_st_generator::generate_typedef(t_typedef* ttypedef) {
+ (void)ttypedef;
+}
+
+void t_st_generator::st_class_def(std::ostream& out, string name) {
+ out << "Object subclass: #" << prefix(name) << endl;
+ indent_up();
+ out << indent() << "instanceVariableNames: ''" << endl << indent() << "classVariableNames: ''"
+ << endl << indent() << "poolDictionaries: ''" << endl << indent() << "category: '"
+ << generated_category() << "'!" << endl << endl;
+}
+
+void t_st_generator::st_method(std::ostream& out, string cls, string name) {
+ st_method(out, cls, name, "as yet uncategorized");
+}
+
+void t_st_generator::st_class_method(std::ostream& out, string cls, string name) {
+ st_method(out, cls + " class", name);
+}
+
+void t_st_generator::st_class_method(std::ostream& out, string cls, string name, string category) {
+ st_method(out, cls, name, category);
+}
+
+void t_st_generator::st_method(std::ostream& out, string cls, string name, string category) {
+ char timestr[50];
+ time_t rawtime;
+ struct tm* tinfo;
+
+ time(&rawtime);
+ tinfo = localtime(&rawtime);
+ strftime(timestr, 50, "%m/%d/%Y %H:%M", tinfo);
+
+ out << "!" << prefix(cls) << " methodsFor: '" + category + "' stamp: 'thrift " << timestr
+ << "'!\n" << name << endl;
+
+ indent_up();
+ out << indent();
+}
+
+void t_st_generator::st_close_method(std::ostream& out) {
+ out << "! !" << endl << endl;
+ indent_down();
+}
+
+void t_st_generator::st_setter(std::ostream& out,
+ string cls,
+ string name,
+ string type = "anObject") {
+ st_method(out, cls, name + ": " + type);
+ out << name << " := " + type;
+ st_close_method(out);
+}
+
+void t_st_generator::st_getter(std::ostream& out, string cls, string name) {
+ st_method(out, cls, name + "");
+ out << "^ " << name;
+ st_close_method(out);
+}
+
+void t_st_generator::st_accessors(std::ostream& out,
+ string cls,
+ string name,
+ string type = "anObject") {
+ st_setter(out, cls, name, type);
+ st_getter(out, cls, name);
+}
+
+void t_st_generator::generate_class_side_definition() {
+ f_ << prefix(class_name()) << " class" << endl << "\tinstanceVariableNames: 'constants enums'!"
+ << endl << endl;
+
+ st_accessors(f_, class_name() + " class", "enums");
+ st_accessors(f_, class_name() + " class", "constants");
+
+ f_ << prefix(class_name()) << " enums: Dictionary new!" << endl;
+ f_ << prefix(class_name()) << " constants: Dictionary new!" << endl;
+
+ f_ << endl;
+}
+
+/**
+ * Generates code for an enumerated type. Done using a class to scope
+ * the values.
+ *
+ * @param tenum The enumeration
+ */
+void t_st_generator::generate_enum(t_enum* tenum) {
+ string cls_name = program_name_ + capitalize(tenum->get_name());
+
+ f_ << prefix(class_name()) << " enums at: '" << tenum->get_name() << "' put: ["
+ << "(Dictionary new " << endl;
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ int value = (*c_iter)->get_value();
+ f_ << "\tat: '" << (*c_iter)->get_name() << "' put: " << value << ";" << endl;
+ }
+
+ f_ << "\tyourself)]!" << endl << endl;
+}
+
+/**
+ * Generate a constant value
+ */
+void t_st_generator::generate_const(t_const* tconst) {
+ t_type* type = tconst->get_type();
+ string name = tconst->get_name();
+ t_const_value* value = tconst->get_value();
+
+ f_ << prefix(class_name()) << " constants at: '" << name << "' put: ["
+ << render_const_value(type, value) << "]!" << endl << endl;
+}
+
+/**
+ * Prints the value of a constant with the given type. Note that type checking
+ * is NOT performed in this function as it is always run beforehand using the
+ * validate_types method in main.cc
+ */
+string t_st_generator::render_const_value(t_type* type, t_const_value* value) {
+ type = get_true_type(type);
+ std::ostringstream out;
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << '"' << get_escaped_string(value) << '"';
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << (value->get_integer() > 0 ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << value->get_integer();
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ indent(out) << value->get_integer();
+ } else if (type->is_struct() || type->is_xception()) {
+ out << "(" << capitalize(type->get_name()) << " new " << endl;
+ indent_up();
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ out << indent() << v_iter->first->get_string() << ": "
+ << render_const_value(field_type, v_iter->second) << ";" << endl;
+ }
+ out << indent() << "yourself)";
+
+ indent_down();
+ } else if (type->is_map()) {
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+ out << "(Dictionary new" << endl;
+ indent_up();
+ indent_up();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << indent();
+ out << "at: " << render_const_value(ktype, v_iter->first);
+ out << " put: ";
+ out << render_const_value(vtype, v_iter->second);
+ out << ";" << endl;
+ }
+ out << indent() << indent() << "yourself)";
+ indent_down();
+ indent_down();
+ } else if (type->is_list() || type->is_set()) {
+ t_type* etype;
+ if (type->is_list()) {
+ etype = ((t_list*)type)->get_elem_type();
+ } else {
+ etype = ((t_set*)type)->get_elem_type();
+ }
+ if (type->is_set()) {
+ out << "(Set new" << endl;
+ } else {
+ out << "(OrderedCollection new" << endl;
+ }
+ indent_up();
+ indent_up();
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ out << indent() << indent();
+ out << "add: " << render_const_value(etype, *v_iter);
+ out << ";" << endl;
+ }
+ out << indent() << indent() << "yourself)";
+ indent_down();
+ indent_down();
+ } else {
+ throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name();
+ }
+ return out.str();
+}
+
+/**
+ * Generates a Smalltalk struct
+ */
+void t_st_generator::generate_struct(t_struct* tstruct) {
+ generate_st_struct(f_, tstruct, false);
+}
+
+/**
+ * Generates a struct definition for a thrift exception. Basically the same
+ * as a struct but extends the Exception class.
+ *
+ * @param txception The struct definition
+ */
+void t_st_generator::generate_xception(t_struct* txception) {
+ generate_st_struct(f_, txception, true);
+}
+
+/**
+ * Generates a smalltalk class to represent a struct
+ */
+void t_st_generator::generate_st_struct(std::ostream& out,
+ t_struct* tstruct,
+ bool is_exception = false) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ if (is_exception)
+ out << "Error";
+ else
+ out << "Object";
+
+ out << " subclass: #" << prefix(type_name(tstruct)) << endl << "\tinstanceVariableNames: '";
+
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (m_iter != members.begin())
+ out << " ";
+ out << camelcase((*m_iter)->get_name());
+ }
+ }
+
+ out << "'\n"
+ << "\tclassVariableNames: ''\n"
+ << "\tpoolDictionaries: ''\n"
+ << "\tcategory: '" << generated_category() << "'!\n\n";
+
+ generate_accessors(out, tstruct);
+}
+
+bool t_st_generator::is_vowel(char c) {
+ switch (tolower(c)) {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ return true;
+ }
+ return false;
+}
+
+string t_st_generator::a_type(t_type* type) {
+ string prefix;
+
+ if (is_vowel(type_name(type)[0]))
+ prefix = "an";
+ else
+ prefix = "a";
+
+ return prefix + capitalize(type_name(type));
+}
+
+void t_st_generator::generate_accessors(std::ostream& out, t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ string type;
+ string prefix;
+
+ if (members.size() > 0) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ st_accessors(out,
+ capitalize(type_name(tstruct)),
+ camelcase((*m_iter)->get_name()),
+ a_type((*m_iter)->get_type()));
+ }
+ out << endl;
+ }
+}
+
+/**
+ * Generates a thrift service.
+ *
+ * @param tservice The service definition
+ */
+void t_st_generator::generate_service(t_service* tservice) {
+ generate_service_client(tservice);
+ // generate_service_server(tservice);
+}
+
+string t_st_generator::temp_name() {
+ std::ostringstream out;
+ out << "temp" << temporary_var++;
+ return out.str();
+}
+
+string t_st_generator::map_writer(t_map* tmap, string fname) {
+ std::ostringstream out;
+ string key = temp_name();
+ string val = temp_name();
+
+ out << "[oprot writeMapBegin: (TMap new keyType: " << type_to_enum(tmap->get_key_type())
+ << "; valueType: " << type_to_enum(tmap->get_val_type()) << "; size: " << fname << " size)."
+ << endl;
+ indent_up();
+
+ out << indent() << fname << " keysAndValuesDo: [:" << key << " :" << val << " |" << endl;
+ indent_up();
+
+ out << indent() << write_val(tmap->get_key_type(), key) << "." << endl << indent()
+ << write_val(tmap->get_val_type(), val);
+ indent_down();
+
+ out << "]." << endl << indent() << "oprot writeMapEnd] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::map_reader(t_map* tmap) {
+ std::ostringstream out;
+ string desc = temp_name();
+ string val = temp_name();
+
+ out << "[|" << desc << " " << val << "| " << endl;
+ indent_up();
+
+ out << indent() << desc << " := iprot readMapBegin." << endl << indent() << val
+ << " := Dictionary new." << endl << indent() << desc << " size timesRepeat: [" << endl;
+
+ indent_up();
+ out << indent() << val << " at: " << read_val(tmap->get_key_type())
+ << " put: " << read_val(tmap->get_val_type());
+ indent_down();
+
+ out << "]." << endl << indent() << "iprot readMapEnd." << endl << indent() << val << "] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::list_writer(t_list* tlist, string fname) {
+ std::ostringstream out;
+ string val = temp_name();
+
+ out << "[oprot writeListBegin: (TList new elemType: " << type_to_enum(tlist->get_elem_type())
+ << "; size: " << fname << " size)." << endl;
+ indent_up();
+
+ out << indent() << fname << " do: [:" << val << "|" << endl;
+ indent_up();
+
+ out << indent() << write_val(tlist->get_elem_type(), val) << endl;
+ indent_down();
+
+ out << "]." << endl << indent() << "oprot writeListEnd] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::list_reader(t_list* tlist) {
+ std::ostringstream out;
+ string desc = temp_name();
+ string val = temp_name();
+
+ out << "[|" << desc << " " << val << "| " << desc << " := iprot readListBegin." << endl;
+ indent_up();
+
+ out << indent() << val << " := OrderedCollection new." << endl << indent() << desc
+ << " size timesRepeat: [" << endl;
+
+ indent_up();
+ out << indent() << val << " add: " << read_val(tlist->get_elem_type());
+ indent_down();
+
+ out << "]." << endl << indent() << "iprot readListEnd." << endl << indent() << val << "] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::set_writer(t_set* tset, string fname) {
+ std::ostringstream out;
+ string val = temp_name();
+
+ out << "[oprot writeSetBegin: (TSet new elemType: " << type_to_enum(tset->get_elem_type())
+ << "; size: " << fname << " size)." << endl;
+ indent_up();
+
+ out << indent() << fname << " do: [:" << val << "|" << endl;
+ indent_up();
+
+ out << indent() << write_val(tset->get_elem_type(), val) << endl;
+ indent_down();
+
+ out << "]." << endl << indent() << "oprot writeSetEnd] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::set_reader(t_set* tset) {
+ std::ostringstream out;
+ string desc = temp_name();
+ string val = temp_name();
+
+ out << "[|" << desc << " " << val << "| " << desc << " := iprot readSetBegin." << endl;
+ indent_up();
+
+ out << indent() << val << " := Set new." << endl << indent() << desc << " size timesRepeat: ["
+ << endl;
+
+ indent_up();
+ out << indent() << val << " add: " << read_val(tset->get_elem_type());
+ indent_down();
+
+ out << "]." << endl << indent() << "iprot readSetEnd." << endl << indent() << val << "] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::struct_writer(t_struct* tstruct, string sname) {
+ std::ostringstream out;
+ const vector<t_field*>& fields = tstruct->get_sorted_members();
+ vector<t_field*>::const_iterator fld_iter;
+
+ out << "[oprot writeStructBegin: "
+ << "(TStruct new name: '" + tstruct->get_name() + "')." << endl;
+ indent_up();
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ bool optional = (*fld_iter)->get_req() == t_field::T_OPTIONAL;
+ string fname = camelcase((*fld_iter)->get_name());
+ string accessor = sname + " " + camelcase(fname);
+
+ if (optional) {
+ out << indent() << accessor << " ifNotNil: [" << endl;
+ indent_up();
+ }
+
+ out << indent() << "oprot writeFieldBegin: (TField new name: '" << fname
+ << "'; type: " << type_to_enum((*fld_iter)->get_type())
+ << "; id: " << (*fld_iter)->get_key() << ")." << endl;
+
+ out << indent() << write_val((*fld_iter)->get_type(), accessor) << "." << endl << indent()
+ << "oprot writeFieldEnd";
+
+ if (optional) {
+ out << "]";
+ indent_down();
+ }
+
+ out << "." << endl;
+ }
+
+ out << indent() << "oprot writeFieldStop; writeStructEnd] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::struct_reader(t_struct* tstruct, string clsName = "") {
+ std::ostringstream out;
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+ string val = temp_name();
+ string desc = temp_name();
+ string found = temp_name();
+
+ if (clsName.size() == 0) {
+ clsName = tstruct->get_name();
+ }
+
+ out << "[|" << desc << " " << val << "|" << endl;
+ indent_up();
+
+ // This is nasty, but without it we'll break things by prefixing TResult.
+ string name = ((capitalize(clsName) == "TResult") ? capitalize(clsName) : prefix(clsName));
+ out << indent() << val << " := " << name << " new." << endl;
+
+ out << indent() << "iprot readStructBegin." << endl << indent() << "[" << desc
+ << " := iprot readFieldBegin." << endl << indent() << desc
+ << " type = TType stop] whileFalse: [|" << found << "|" << endl;
+ indent_up();
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ out << indent() << desc << " id = " << (*fld_iter)->get_key() << " ifTrue: [" << endl;
+ indent_up();
+
+ out << indent() << found << " := true." << endl << indent() << val << " "
+ << camelcase((*fld_iter)->get_name()) << ": " << read_val((*fld_iter)->get_type());
+ indent_down();
+
+ out << "]." << endl;
+ }
+
+ out << indent() << found << " ifNil: [iprot skip: " << desc << " type]]." << endl;
+ indent_down();
+
+ out << indent() << "oprot readStructEnd." << endl << indent() << val << "] value";
+ indent_down();
+
+ return out.str();
+}
+
+string t_st_generator::write_val(t_type* t, string fname) {
+ t = get_true_type(t);
+
+ if (t->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)t)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_DOUBLE:
+ return "iprot writeDouble: " + fname + " asFloat";
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ return "iprot write" + capitalize(type_name(t)) + ": " + fname + " asInteger";
+ default:
+ return "iprot write" + capitalize(type_name(t)) + ": " + fname;
+ }
+ } else if (t->is_map()) {
+ return map_writer((t_map*)t, fname);
+ } else if (t->is_struct() || t->is_xception()) {
+ return struct_writer((t_struct*)t, fname);
+ } else if (t->is_list()) {
+ return list_writer((t_list*)t, fname);
+ } else if (t->is_set()) {
+ return set_writer((t_set*)t, fname);
+ } else if (t->is_enum()) {
+ return "iprot writeI32: " + fname;
+ } else {
+ throw "Sorry, I don't know how to write this: " + type_name(t);
+ }
+}
+
+string t_st_generator::read_val(t_type* t) {
+ t = get_true_type(t);
+
+ if (t->is_base_type()) {
+ return "iprot read" + capitalize(type_name(t));
+ } else if (t->is_map()) {
+ return map_reader((t_map*)t);
+ } else if (t->is_struct() || t->is_xception()) {
+ return struct_reader((t_struct*)t);
+ } else if (t->is_list()) {
+ return list_reader((t_list*)t);
+ } else if (t->is_set()) {
+ return set_reader((t_set*)t);
+ } else if (t->is_enum()) {
+ return "iprot readI32";
+ } else {
+ throw "Sorry, I don't know how to read this: " + type_name(t);
+ }
+}
+
+void t_st_generator::generate_send_method(t_function* function) {
+ string funname = function->get_name();
+ string signature = function_signature(function);
+ t_struct* arg_struct = function->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator fld_iter;
+
+ st_method(f_, client_class_name(), "send" + capitalize(signature));
+ f_ << "oprot writeMessageBegin:" << endl;
+ indent_up();
+
+ f_ << indent() << "(TCallMessage new" << endl;
+ indent_up();
+
+ f_ << indent() << "name: '" << funname << "'; " << endl << indent() << "seqid: self nextSeqid)."
+ << endl;
+ indent_down();
+ indent_down();
+
+ f_ << indent() << "oprot writeStructBegin: "
+ << "(TStruct new name: '" + capitalize(camelcase(funname)) + "_args')." << endl;
+
+ for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
+ string fname = camelcase((*fld_iter)->get_name());
+
+ f_ << indent() << "oprot writeFieldBegin: (TField new name: '" << fname
+ << "'; type: " << type_to_enum((*fld_iter)->get_type()) << "; id: " << (*fld_iter)->get_key()
+ << ")." << endl;
+
+ f_ << indent() << write_val((*fld_iter)->get_type(), fname) << "." << endl << indent()
+ << "oprot writeFieldEnd." << endl;
+ }
+
+ f_ << indent() << "oprot writeFieldStop; writeStructEnd; writeMessageEnd." << endl;
+ f_ << indent() << "oprot transport flush";
+
+ st_close_method(f_);
+}
+
+// We only support receiving TResult structures (so this won't work on the server side)
+void t_st_generator::generate_recv_method(t_function* function) {
+ string funname = camelcase(function->get_name());
+ string signature = function_signature(function);
+
+ t_struct result(program_, "TResult");
+ t_field success(function->get_returntype(), "success", 0);
+ result.append(&success);
+
+ t_struct* xs = function->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ // duplicate the field, but call it "exception"... we don't need a dynamic name
+ t_field* exception = new t_field((*f_iter)->get_type(), "exception", (*f_iter)->get_key());
+ result.append(exception);
+ }
+
+ st_method(f_, client_class_name(), "recv" + capitalize(funname));
+ f_ << "| f msg res | " << endl << indent() << "msg := oprot readMessageBegin." << endl << indent()
+ << "self validateRemoteMessage: msg." << endl << indent()
+ << "res := " << struct_reader(&result) << "." << endl << indent() << "oprot readMessageEnd."
+ << endl << indent() << "oprot transport flush." << endl << indent()
+ << "res exception ifNotNil: [res exception signal]." << endl << indent() << "^ res";
+ st_close_method(f_);
+}
+
+string t_st_generator::function_types_comment(t_function* fn) {
+ std::ostringstream out;
+ const vector<t_field*>& fields = fn->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ out << "\"";
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ out << camelcase((*f_iter)->get_name()) << ": " << type_name((*f_iter)->get_type());
+ if ((f_iter + 1) != fields.end()) {
+ out << ", ";
+ }
+ }
+
+ out << "\"";
+
+ return out.str();
+}
+
+/**
+ * Generates a service client definition.
+ *
+ * @param tservice The service to generate a server for.
+ */
+void t_st_generator::generate_service_client(t_service* tservice) {
+ string extends = "";
+ string extends_client = "TClient";
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ if (tservice->get_extends() != NULL) {
+ extends = type_name(tservice->get_extends());
+ extends_client = extends + "Client";
+ }
+
+ f_ << extends_client << " subclass: #" << prefix(client_class_name()) << endl
+ << "\tinstanceVariableNames: ''\n"
+ << "\tclassVariableNames: ''\n"
+ << "\tpoolDictionaries: ''\n"
+ << "\tcategory: '" << generated_category() << "'!\n\n";
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string funname = camelcase((*f_iter)->get_name());
+ string signature = function_signature(*f_iter);
+
+ st_method(f_, client_class_name(), signature);
+ f_ << function_types_comment(*f_iter) << endl << indent() << "self send"
+ << capitalize(signature) << "." << endl;
+
+ if (!(*f_iter)->is_oneway()) {
+ f_ << indent() << "^ self recv" << capitalize(funname) << " success " << endl;
+ }
+
+ st_close_method(f_);
+
+ generate_send_method(*f_iter);
+ if (!(*f_iter)->is_oneway()) {
+ generate_recv_method(*f_iter);
+ }
+ }
+}
+
+/**
+ * Renders a function signature of the form 'type name(args)'
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_st_generator::function_signature(t_function* tfunction) {
+ return camelcase(tfunction->get_name()) + capitalize(argument_list(tfunction->get_arglist()));
+}
+
+/**
+ * Renders a field list
+ */
+string t_st_generator::argument_list(t_struct* tstruct) {
+ string result = "";
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ result += " ";
+ }
+ string name = camelcase((*f_iter)->get_name());
+ result += name + ": " + name;
+ }
+ return result;
+}
+
+string t_st_generator::type_name(t_type* ttype) {
+ string prefix = "";
+ t_program* program = ttype->get_program();
+ if (program != NULL && program != program_) {
+ if (!ttype->is_service()) {
+ prefix = program->get_name() + "_types.";
+ }
+ }
+
+ string name = ttype->get_name();
+ if (ttype->is_struct() || ttype->is_xception()) {
+ name = capitalize(ttype->get_name());
+ }
+
+ return prefix + name;
+}
+
+/* Convert t_type to Smalltalk type code */
+string t_st_generator::type_to_enum(t_type* type) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return "TType string";
+ case t_base_type::TYPE_BOOL:
+ return "TType bool";
+ case t_base_type::TYPE_I8:
+ return "TType byte";
+ case t_base_type::TYPE_I16:
+ return "TType i16";
+ case t_base_type::TYPE_I32:
+ return "TType i32";
+ case t_base_type::TYPE_I64:
+ return "TType i64";
+ case t_base_type::TYPE_DOUBLE:
+ return "TType double";
+ }
+ } else if (type->is_enum()) {
+ return "TType i32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return "TType struct";
+ } else if (type->is_map()) {
+ return "TType map";
+ } else if (type->is_set()) {
+ return "TType set";
+ } else if (type->is_list()) {
+ return "TType list";
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+THRIFT_REGISTER_GENERATOR(st, "Smalltalk", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc
new file mode 100644
index 000000000..eb746c109
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc
@@ -0,0 +1,3199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <set>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/platform.h"
+#include "thrift/generate/t_oop_generator.h"
+
+using std::map;
+using std::ostream;
+using std::ostringstream;
+using std::set;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * Swift 3 code generator.
+ *
+ * Designed from the Swift/Cocoa code generator(s)
+ */
+class t_swift_generator : public t_oop_generator {
+public:
+ t_swift_generator(t_program* program,
+ const map<string, string>& parsed_options,
+ const string& option_string)
+ : t_oop_generator(program) {
+ (void)option_string;
+ map<string, string>::const_iterator iter;
+
+ log_unexpected_ = false;
+ async_clients_ = false;
+ debug_descriptions_ = false;
+ no_strict_ = false;
+ namespaced_ = false;
+ gen_cocoa_ = false;
+ promise_kit_ = false;
+ safe_enums_ = false;
+
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("log_unexpected") == 0) {
+ log_unexpected_ = true;
+ } else if( iter->first.compare("async_clients") == 0) {
+ async_clients_ = true;
+ } else if( iter->first.compare("no_strict") == 0) {
+ no_strict_ = true;
+ } else if( iter->first.compare("debug_descriptions") == 0) {
+ debug_descriptions_ = true;
+ } else if( iter->first.compare("namespaced") == 0) {
+ namespaced_ = true;
+ } else if( iter->first.compare("cocoa") == 0) {
+ gen_cocoa_ = true;
+ } else if( iter->first.compare("safe_enums") == 0) {
+ safe_enums_ = true;
+ } else if( iter->first.compare("promise_kit") == 0) {
+ if (gen_cocoa_ == false) {
+ throw "PromiseKit only available with Swift 2.x, use `cocoa` option" + iter->first;
+ }
+ promise_kit_ = true;
+ } else {
+ throw "unknown option swift:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-swift";
+ }
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ void generate_consts(vector<t_const*> consts) override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_struct(t_struct* tstruct) override;
+ void generate_xception(t_struct* txception) override;
+ void generate_service(t_service* tservice) override;
+
+
+ void render_const_value(ostream& out,
+ t_type* type,
+ t_const_value* value);
+
+ void generate_swift_struct(ostream& out,
+ t_struct* tstruct,
+ bool is_private);
+
+ void generate_swift_struct_init(ostream& out,
+ t_struct* tstruct,
+ bool all,
+ bool is_private);
+
+ void generate_swift_struct_implementation(ostream& out,
+ t_struct* tstruct,
+ bool is_result,
+ bool is_private);
+ void generate_swift_struct_hashable_extension(ostream& out,
+ t_struct* tstruct,
+ bool is_private);
+ void generate_swift_struct_equatable_extension(ostream& out,
+ t_struct* tstruct,
+ bool is_private);
+ void generate_swift_struct_thrift_extension(ostream& out,
+ t_struct* tstruct,
+ bool is_result,
+ bool is_private);
+ void generate_swift_struct_reader(ostream& out, t_struct* tstruct, bool is_private);
+
+
+ void generate_swift_struct_printable_extension(ostream& out, t_struct* tstruct);
+ void generate_swift_union_reader(ostream& out, t_struct* tstruct);
+
+ string function_result_helper_struct_type(t_service *tservice, t_function* tfunction);
+ string function_args_helper_struct_type(t_service* tservice, t_function* tfunction);
+ void generate_function_helpers(t_service *tservice, t_function* tfunction);
+
+ /**
+ * Service-level generation functions
+ */
+
+ void generate_swift_service_protocol(ostream& out, t_service* tservice);
+ void generate_swift_service_protocol_async(ostream& out, t_service* tservice);
+
+ void generate_swift_service_client(ostream& out, t_service* tservice);
+ void generate_swift_service_client_async(ostream& out, t_service* tservice);
+
+ void generate_swift_service_client_send_function_implementation(ostream& out,
+ t_service* tservice,
+ t_function* tfunction,
+ bool needs_protocol);
+ void generate_swift_service_client_send_function_invocation(ostream& out, t_function* tfunction);
+ void generate_swift_service_client_send_async_function_invocation(ostream& out,
+ t_function* tfunction);
+ void generate_swift_service_client_recv_function_implementation(ostream& out,
+ t_service* tservice,
+ t_function* tfunction,
+ bool needs_protocol);
+ void generate_swift_service_client_implementation(ostream& out, t_service* tservice);
+ void generate_swift_service_client_async_implementation(ostream& out, t_service* tservice);
+
+ void generate_swift_service_server(ostream& out, t_service* tservice);
+ void generate_swift_service_server_implementation(ostream& out, t_service* tservice);
+ void generate_swift_service_helpers(t_service* tservice);
+
+ /**
+ * Helper rendering functions
+ */
+
+ string swift_imports();
+ string swift_thrift_imports();
+ string type_name(t_type* ttype, bool is_optional=false, bool is_forced=false);
+ string base_type_name(t_base_type* tbase);
+ string declare_property(t_field* tfield, bool is_private);
+ string function_signature(t_function* tfunction);
+ string async_function_signature(t_function* tfunction);
+
+
+ string argument_list(t_struct* tstruct, string protocol_name, bool is_internal);
+ string type_to_enum(t_type* ttype, bool qualified=false);
+ string maybe_escape_identifier(const string& identifier);
+ void populate_reserved_words();
+ /** Swift 3 specific */
+ string enum_case_name(t_enum_value* tenum_case, bool declaration);
+ string enum_const_name(string enum_identifier);
+ void function_docstring(ostream& out, t_function* tfunction);
+ void async_function_docstring(ostream& out, t_function* tfunction);
+ void generate_docstring(ostream& out, string& doc);
+
+ /** Swift 2/Cocoa carryover */
+ string promise_function_signature(t_function* tfunction);
+ string function_name(t_function* tfunction);
+ void generate_old_swift_struct_writer(ostream& out,t_struct* tstruct, bool is_private);
+ void generate_old_swift_struct_result_writer(ostream& out, t_struct* tstruct);
+
+ /** Swift 2/Cocoa backwards compatibility*/
+ void generate_old_enum(t_enum* tenum);
+ void generate_old_swift_struct(ostream& out,
+ t_struct* tstruct,
+ bool is_private);
+ void generate_old_swift_service_client_async_implementation(ostream& out,
+ t_service* tservice);
+
+ static std::string get_real_swift_module(const t_program* program) {
+ std::string real_module = program->get_namespace("swift");
+ if (real_module.empty()) {
+ return program->get_name();
+ }
+ return real_module;
+ }
+private:
+
+ void block_open(ostream& out) {
+ out << " {" << endl;
+ indent_up();
+ }
+
+ void block_close(ostream& out, bool end_line=true) {
+ indent_down();
+ indent(out) << "}";
+ if (end_line) out << endl;
+ }
+
+ bool field_is_optional(t_field* tfield) {
+ bool opt = tfield->get_req() == t_field::T_OPTIONAL;
+ if (tfield->annotations_.find("swift.nullable") != tfield->annotations_.end() && tfield->get_req() != t_field::T_REQUIRED) {
+ opt = true;
+ }
+ if (gen_cocoa_) { // Backwards compatibility, only if its actually "optional"
+ opt = tfield->get_req() == t_field::T_OPTIONAL;
+ }
+ return opt;
+ }
+
+ bool struct_has_required_fields(t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!field_is_optional(*m_iter)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool struct_has_optional_fields(t_struct* tstruct) {
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (field_is_optional(*m_iter)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ string constants_declarations_;
+
+ /**
+ * File streams
+ */
+
+ ofstream_with_content_based_conditional_update f_decl_;
+ ofstream_with_content_based_conditional_update f_impl_;
+
+ bool log_unexpected_;
+ bool async_clients_;
+
+ bool debug_descriptions_;
+ bool no_strict_;
+ bool namespaced_;
+ bool safe_enums_;
+ set<string> swift_reserved_words_;
+
+ /** Swift 2/Cocoa compatibility */
+ bool gen_cocoa_;
+ bool promise_kit_;
+
+};
+
+/**
+ * Prepares for file generation by opening up the necessary file output
+ * streams.
+ */
+void t_swift_generator::init_generator() {
+ // Make output directory
+ string module = get_real_swift_module(program_);
+ string out_dir = get_out_dir();
+ string module_path = out_dir;
+ string name = program_name_;
+ if (namespaced_ && !module.empty()) {
+ module_path = module_path + "/" + module;
+ name = module;
+ }
+ MKDIR(module_path.c_str());
+
+ populate_reserved_words();
+
+ // we have a .swift declarations file...
+ string f_decl_name = name + ".swift";
+ string f_decl_fullname = module_path + "/" + f_decl_name;
+ f_decl_.open(f_decl_fullname.c_str());
+
+ f_decl_ << autogen_comment() << endl;
+
+ f_decl_ << swift_imports() << swift_thrift_imports() << endl;
+
+ // ...and a .swift implementation extensions file
+ string f_impl_name = name + "+Exts.swift";
+ string f_impl_fullname = module_path + "/" + f_impl_name;
+ f_impl_.open(f_impl_fullname.c_str());
+
+ f_impl_ << autogen_comment() << endl;
+
+ f_impl_ << swift_imports() << swift_thrift_imports() << endl;
+
+}
+
+/**
+ * Prints standard Cocoa imports
+ *
+ * @return List of imports for Cocoa libraries
+ */
+string t_swift_generator::swift_imports() {
+
+ vector<string> includes_list;
+ includes_list.emplace_back("Foundation");
+
+ ostringstream includes;
+
+ vector<string>::const_iterator i_iter;
+ for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) {
+ includes << "import " << *i_iter << endl;
+ }
+
+ if (namespaced_) {
+ const vector<t_program*>& program_includes = program_->get_includes();
+ for (auto program_include : program_includes) {
+ includes << ("import " + get_real_swift_module(program_include)) << endl;
+ }
+ }
+ includes << endl;
+
+ return includes.str();
+}
+
+/**
+ * Prints Thrift runtime imports
+ *
+ * @return List of imports necessary for Thrift runtime
+ */
+string t_swift_generator::swift_thrift_imports() {
+
+ vector<string> includes_list;
+ includes_list.emplace_back("Thrift");
+
+ if (gen_cocoa_ && promise_kit_) {
+ includes_list.emplace_back("PromiseKit");
+ }
+
+ ostringstream includes;
+
+ vector<string>::const_iterator i_iter;
+ for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) {
+ includes << "import " << *i_iter << endl;
+ }
+
+ includes << endl;
+
+ return includes.str();
+}
+
+/**
+ * Finish up generation.
+ */
+void t_swift_generator::close_generator() {
+ // stick our constants declarations at the end of the header file
+ // since they refer to things we are defining.
+ f_decl_ << constants_declarations_ << endl;
+}
+
+/**
+ * Generates a typedef. This is just a simple 1-liner in Swift
+ *
+ * @param ttypedef The type definition
+ */
+void t_swift_generator::generate_typedef(t_typedef* ttypedef) {
+ f_decl_ << indent() << "public typealias " << ttypedef->get_symbolic()
+ << " = " << type_name(ttypedef->get_type()) << endl;
+ f_decl_ << endl;
+}
+
+
+/**
+ * Generates code for an enumerated type. In Swift, this is
+ * essentially the same as the thrift definition itself, using
+ * Swift syntax. Conforms to TEnum which
+ * implementes read/write.
+ *
+ * @param tenum The enumeration
+ */
+void t_swift_generator::generate_enum(t_enum* tenum) {
+ if (gen_cocoa_) {
+ generate_old_enum(tenum);
+ return;
+ }
+ f_decl_ << indent() << "public enum " << tenum->get_name() << " : TEnum";
+ block_open(f_decl_);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ f_decl_ << indent() << "case " << enum_case_name((*c_iter), true) << endl;
+ }
+
+ // unknown associated value case for safety and similar behavior to other languages
+ if (safe_enums_) {
+ f_decl_ << indent() << "case unknown(Int32)" << endl;
+ }
+ f_decl_ << endl;
+
+ // TSerializable read(from:)
+ f_decl_ << indent() << "public static func read(from proto: TProtocol) throws -> "
+ << tenum->get_name();
+ block_open(f_decl_);
+ f_decl_ << indent() << "let raw: Int32 = try proto.read()" << endl;
+ f_decl_ << indent() << "let new = " << tenum->get_name() << "(rawValue: raw)" << endl;
+
+ f_decl_ << indent() << "if let unwrapped = new {" << endl;
+ indent_up();
+ f_decl_ << indent() << "return unwrapped" << endl;
+ indent_down();
+ f_decl_ << indent() << "} else {" << endl;
+ indent_up();
+ f_decl_ << indent() << "throw TProtocolError(error: .invalidData," << endl;
+ f_decl_ << indent() << " message: \"Invalid enum value (\\(raw)) for \\("
+ << tenum->get_name() << ".self)\")" << endl;
+ indent_down();
+ f_decl_ << indent() << "}" << endl;
+ block_close(f_decl_);
+
+ // empty init for TSerializable
+ f_decl_ << endl;
+ f_decl_ << indent() << "public init()";
+ block_open(f_decl_);
+
+ f_decl_ << indent() << "self = ." << enum_case_name(constants.front(), false) << endl;
+ block_close(f_decl_);
+ f_decl_ << endl;
+
+ // rawValue getter
+ f_decl_ << indent() << "public var rawValue: Int32";
+ block_open(f_decl_);
+ f_decl_ << indent() << "switch self {" << endl;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ f_decl_ << indent() << "case ." << enum_case_name((*c_iter), true)
+ << ": return " << (*c_iter)->get_value() << endl;
+ }
+ if (safe_enums_) {
+ f_decl_ << indent() << "case .unknown(let value): return value" << endl;
+ }
+ f_decl_ << indent() << "}" << endl;
+ block_close(f_decl_);
+ f_decl_ << endl;
+
+ // convenience rawValue initalizer
+ f_decl_ << indent() << "public init?(rawValue: Int32)";
+ block_open(f_decl_);
+ f_decl_ << indent() << "switch rawValue {" << endl;;
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ f_decl_ << indent() << "case " << (*c_iter)->get_value()
+ << ": self = ." << enum_case_name((*c_iter), true) << endl;
+ }
+ if (!safe_enums_) {
+ f_decl_ << indent() << "default: return nil" << endl;
+ } else {
+ f_decl_ << indent() << "default: self = .unknown(rawValue)" << endl;
+ }
+ f_decl_ << indent() << "}" << endl;
+ block_close(f_decl_);
+
+
+
+
+ block_close(f_decl_);
+ f_decl_ << endl;
+}
+
+/**
+ * Generates code for an enumerated type. This is for Swift 2.x/Cocoa
+ * backwards compatibility
+ *
+ * @param tenum The enumeration
+ */
+void t_swift_generator::generate_old_enum(t_enum* tenum) {
+ f_decl_ << indent() << "public enum " << tenum->get_name() << " : Int32";
+ block_open(f_decl_);
+
+ vector<t_enum_value*> constants = tenum->get_constants();
+ vector<t_enum_value*>::iterator c_iter;
+
+ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
+ f_decl_ << indent() << "case " << (*c_iter)->get_name()
+ << " = " << (*c_iter)->get_value() << endl;
+ }
+
+ f_decl_ << endl;
+ f_decl_ << indent() << "public init() { self.init(rawValue: " << constants.front()->get_value() << ")! }" << endl;
+
+ block_close(f_decl_);
+ f_decl_ << endl;
+
+ f_impl_ << indent() << "extension " << tenum->get_name() << " : TEnum";
+ block_open(f_impl_);
+
+ f_impl_ << endl;
+
+ f_impl_ << indent() << "public static func readValueFromProtocol(proto: TProtocol) throws -> " << tenum->get_name();
+ block_open(f_impl_);
+ f_impl_ << indent() << "var raw = Int32()" << endl
+ << indent() << "try proto.readI32(&raw)" << endl
+ << indent() << "return " << tenum->get_name() << "(rawValue: raw)!" << endl;
+ block_close(f_impl_);
+ f_impl_ << endl;
+
+ f_impl_ << indent() << "public static func writeValue(value: " << tenum->get_name() << ", toProtocol proto: TProtocol) throws";
+ block_open(f_impl_);
+ f_impl_ << indent() << "try proto.writeI32(value.rawValue)" << endl;
+ block_close(f_impl_);
+ f_impl_ << endl;
+
+ block_close(f_impl_);
+ f_impl_ << endl;
+}
+
+string t_swift_generator::enum_case_name(t_enum_value* tenum_case, bool declaration) {
+ string name = tenum_case->get_name();
+ // force to lowercase for Swift style, maybe escape if its a keyword
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+ if (declaration) {
+ name = maybe_escape_identifier(name);
+ }
+ return name;
+}
+
+/**
+ * Renders a constant enum value by transforming the value portion to lowercase
+ * for Swift style.
+ */
+string t_swift_generator::enum_const_name(string enum_identifier) {
+ string::iterator it;
+ for (it = enum_identifier.begin(); it < enum_identifier.end(); ++it) {
+ if ((*it) == '.') {
+ break;
+ }
+ }
+ std::transform(it, enum_identifier.end(), it, ::tolower);
+ return enum_identifier;
+}
+
+/**
+ * Generates public constants for all Thrift constants.
+ *
+ * @param consts Constants to generate
+ */
+void t_swift_generator::generate_consts(vector<t_const*> consts) {
+
+ ostringstream const_interface;
+
+ // Public constants for base types & strings
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ t_type* type = (*c_iter)->get_type();
+ const_interface << "public let " << capitalize((*c_iter)->get_name()) << " : " << type_name(type) << " = ";
+ render_const_value(const_interface, type, (*c_iter)->get_value());
+ const_interface << endl << endl;
+ }
+
+ // this gets spit into the header file in ::close_generator
+ constants_declarations_ = const_interface.str();
+
+}
+
+/**
+ * Generates a struct definition for a thrift data type. This is a struct
+ * with public members. Optional types are used for optional properties to
+ * allow them to be tested for availability. Separate inits are included for
+ * required properties & all properties.
+ *
+ * Generates extensions to provide conformance to TStruct, TSerializable,
+ * Hashable & Equatable
+ *
+ * @param tstruct The struct definition
+ */
+void t_swift_generator::generate_struct(t_struct* tstruct) {
+ generate_swift_struct(f_decl_, tstruct, false);
+ generate_swift_struct_implementation(f_impl_, tstruct, false, false);
+}
+
+/**
+ * Exceptions are structs, but they conform to Error
+ *
+ * @param tstruct The struct definition
+ */
+void t_swift_generator::generate_xception(t_struct* txception) {
+ generate_swift_struct(f_decl_, txception, false);
+ generate_swift_struct_implementation(f_impl_, txception, false, false);
+}
+
+void t_swift_generator::generate_docstring(ostream& out, string& doc) {
+ if (doc != "") {
+ std::vector<std::string> strings;
+
+ std::string::size_type pos = 0;
+ std::string::size_type prev = 0;
+ while (((pos = doc.find("\n", prev)) != std::string::npos)
+ || ((pos = doc.find("\r", prev)) != std::string::npos)
+ || ((pos = doc.find("\r\n", prev)) != std::string::npos))
+ {
+ strings.push_back(doc.substr(prev, pos - prev));
+ prev = pos + 1;
+ }
+
+ // To get the last substring (or only, if delimiter is not found)
+ strings.push_back(doc.substr(prev));
+
+ vector<string>::const_iterator d_iter;
+ for (d_iter = strings.begin(); d_iter != strings.end(); ++d_iter) {
+ if ((*d_iter) != "") {
+ out << indent() << "/// " << (*d_iter) << endl;
+ }
+ }
+ }
+}
+
+
+
+/**
+ * Generate the interface for a struct. Only properties and
+ * init methods are included.
+ *
+ * @param tstruct The struct definition
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_swift_struct(ostream& out,
+ t_struct* tstruct,
+ bool is_private) {
+
+ if (gen_cocoa_) {
+ generate_old_swift_struct(out, tstruct, is_private);
+ return;
+ }
+ string doc = tstruct->get_doc();
+ generate_docstring(out, doc);
+
+
+ // properties
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+
+ if (tstruct->is_union()) {
+ // special, unions
+ out << indent() << "public enum " << tstruct->get_name();
+ block_open(out);
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << endl;
+ string doc = (*m_iter)->get_doc();
+ generate_docstring(out, doc);
+ out << indent() << "case "
+ << maybe_escape_identifier((*m_iter)->get_name()) << "(val: "
+ << type_name((*m_iter)->get_type(), false) << ")" << endl;
+ }
+ } else {
+ // Normal structs
+
+ string visibility = is_private ? (gen_cocoa_ ? "private" : "fileprivate") : "public";
+
+ out << indent() << visibility << " final class " << tstruct->get_name();
+
+ if (tstruct->is_xception()) {
+ out << " : Swift.Error"; // Error seems to be a common exception name in thrift
+ }
+
+ block_open(out);
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << endl;
+ // TODO: Defaults
+
+ string doc = (*m_iter)->get_doc();
+ generate_docstring(out, doc);
+
+ out << indent() << declare_property(*m_iter, is_private) << endl;
+ }
+
+ out << endl;
+ out << endl;
+
+ if (!struct_has_required_fields(tstruct)) {
+ indent(out) << visibility << " init() { }" << endl;
+ }
+ if (struct_has_required_fields(tstruct)) {
+ generate_swift_struct_init(out, tstruct, false, is_private);
+ }
+ if (struct_has_optional_fields(tstruct)) {
+ generate_swift_struct_init(out, tstruct, true, is_private);
+ }
+ }
+
+ block_close(out);
+
+ out << endl;
+}
+
+/**
+ * Legacy Swift2/Cocoa generator
+ *
+ * @param tstruct
+ * @param is_private
+ */
+
+
+void t_swift_generator::generate_old_swift_struct(ostream& out,
+ t_struct* tstruct,
+ bool is_private) {
+ string visibility = is_private ? "private" : "public";
+
+ out << indent() << visibility << " final class " << tstruct->get_name();
+
+ if (tstruct->is_xception()) {
+ out << " : ErrorType";
+ }
+
+ block_open(out);
+
+ // properties
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ out << endl;
+ out << indent() << declare_property(*m_iter, is_private) << endl;
+ }
+
+ out << endl;
+
+ // init
+
+ indent(out) << visibility << " init()";
+ block_open(out);
+ block_close(out);
+
+ out << endl;
+
+ if (struct_has_required_fields(tstruct)) {
+ generate_swift_struct_init(out, tstruct, false, is_private);
+ }
+ if (struct_has_optional_fields(tstruct)) {
+ generate_swift_struct_init(out, tstruct, true, is_private);
+ }
+
+ block_close(out);
+
+ out << endl;
+}
+
+/**
+ * Generate struct init for properties
+ *
+ * @param tstruct The structure definition
+ * @param all Generate init with all or just required properties
+ * @param is_private
+ * Is the initializer public or private
+ */
+void t_swift_generator::generate_swift_struct_init(ostream& out,
+ t_struct* tstruct,
+ bool all,
+ bool is_private) {
+
+ string visibility = is_private ? (gen_cocoa_ ? "private" : "fileprivate") : "public";
+
+ indent(out) << visibility << " init(";
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ bool first=true;
+ for (m_iter = members.begin(); m_iter != members.end();) {
+ if (all || !field_is_optional(*m_iter)) {
+ if (first) {
+ first = false;
+ }
+ else {
+ out << ", ";
+ }
+ out << (*m_iter)->get_name() << ": "
+ << maybe_escape_identifier(type_name((*m_iter)->get_type(), field_is_optional(*m_iter)));
+ }
+ ++m_iter;
+ }
+ out << ")";
+
+ block_open(out);
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!gen_cocoa_) {
+ bool should_set = all;
+ should_set = should_set || !field_is_optional((*m_iter));
+ if (should_set) {
+ out << indent() << "self." << maybe_escape_identifier((*m_iter)->get_name()) << " = "
+ << maybe_escape_identifier((*m_iter)->get_name()) << endl;
+ }
+ } else {
+ /** legacy Swift2/Cocoa */
+ if (all || (*m_iter)->get_req() == t_field::T_REQUIRED || (*m_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) {
+ out << indent() << "self." << maybe_escape_identifier((*m_iter)->get_name()) << " = "
+ << maybe_escape_identifier((*m_iter)->get_name()) << endl;
+ }
+ }
+ }
+
+ block_close(out);
+
+ out << endl;
+}
+
+/**
+ * Generate the hashable protocol implmentation
+ *
+ * @param tstruct The structure definition
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_swift_struct_hashable_extension(ostream& out,
+ t_struct* tstruct,
+ bool is_private) {
+
+ string visibility = is_private ? (gen_cocoa_ ? "private" : "fileprivate") : "public";
+ indent(out) << "extension " << tstruct->get_name() << " : Hashable";
+ block_open(out);
+ out << endl;
+ indent(out) << visibility << " var hashValue : Int";
+ block_open(out);
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ if (!members.empty()) {
+ indent(out) << "let prime = 31" << endl;
+ indent(out) << "var result = 1" << endl;
+ if (!tstruct->is_union()) {
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* tfield = *m_iter;
+ string accessor = field_is_optional(tfield) ? "?." : ".";
+ string defaultor = field_is_optional(tfield) ? " ?? 0" : "";
+ indent(out) << "result = prime &* result &+ (" << maybe_escape_identifier(tfield->get_name()) << accessor
+ << "hashValue" << defaultor << ")" << endl;
+ }
+ } else {
+ indent(out) << "switch self {" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); m_iter++) {
+ t_field *tfield = *m_iter;
+ indent(out) << "case ." << tfield->get_name() << "(let val): result = prime &* val.hashValue" << endl;
+ }
+ indent(out) << "}" << endl << endl;
+ }
+ indent(out) << "return result" << endl;
+ }
+ else {
+ indent(out) << "return 31" << endl;
+ }
+
+ block_close(out);
+ out << endl;
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generate the equatable protocol implementation
+ *
+ * @param tstruct The structure definition
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_swift_struct_equatable_extension(ostream& out,
+ t_struct* tstruct,
+ bool is_private) {
+
+ string visibility = is_private ? (gen_cocoa_ ? "private" : "fileprivate") : "public";
+
+ indent(out) << visibility << " func ==(lhs: " << type_name(tstruct) << ", rhs: "
+ << type_name(tstruct) << ") -> Bool";
+ block_open(out);
+ indent(out) << "return";
+
+ const vector<t_field*>& members = tstruct->get_members();
+ vector<t_field*>::const_iterator m_iter;
+
+ if (members.size()) {
+ if (!tstruct->is_union()) {
+ out << endl;
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end();) {
+ t_field* tfield = *m_iter;
+ indent(out) << "(lhs." << maybe_escape_identifier(tfield->get_name())
+ << (gen_cocoa_ ? " ?" : " ") << "== rhs." << maybe_escape_identifier(tfield->get_name()) << ")"; // swift 2 ?== operator not in 3?
+ if (++m_iter != members.end()) {
+ out << " &&";
+ }
+ out << endl;
+ }
+ indent_down();
+ } else {
+ block_open(out);
+ indent(out) << "switch (lhs, rhs) {" << endl;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ t_field* tfield = *m_iter;
+ indent(out) << "case (." << tfield->get_name() << "(let lval), ."
+ << tfield->get_name() << "(let rval)): return lval == rval"
+ << endl;
+ }
+ indent(out) << "default: return false" << endl;
+ indent(out) << "}" << endl;
+ indent_down();
+ indent(out) << "}()" << endl;
+ }
+ }
+ else {
+ out << " true" << endl;
+ }
+
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generate struct implementation. Produces extensions that
+ * fulfill the requisite protocols to complete the value.
+ *
+ * @param tstruct The struct definition
+ * @param is_result
+ * If this is a result it needs a different writer
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_swift_struct_implementation(ostream& out,
+ t_struct* tstruct,
+ bool is_result,
+ bool is_private) {
+
+ generate_swift_struct_equatable_extension(out, tstruct, is_private);
+
+ if (!is_private && !is_result && !gen_cocoa_) { // old compiler didn't use debug_descriptions, OR it with gen_cocoa_ so the flag doesn't matter w/ cocoa
+ generate_swift_struct_printable_extension(out, tstruct);
+ }
+
+ generate_swift_struct_hashable_extension(out, tstruct, is_private);
+ generate_swift_struct_thrift_extension(out, tstruct, is_result, is_private);
+
+ out << endl << endl;
+}
+
+/**
+ * Generate the TStruct protocol implementation.
+ *
+ * @param tstruct The structure definition
+ * @param is_result
+ * Is the struct a result value
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_swift_struct_thrift_extension(ostream& out,
+ t_struct* tstruct,
+ bool is_result,
+ bool is_private) {
+
+ indent(out) << "extension " << tstruct->get_name() << " : TStruct";
+
+ block_open(out);
+
+ out << endl;
+ if (!gen_cocoa_) {
+ /** Swift 3, no writer we just write field ID's */
+ string access = (is_private) ? (gen_cocoa_ ? "private" : "fileprivate") : "public";
+ // generate fieldID's dictionary
+ out << indent() << access << " static var fieldIds: [String: Int32]";
+ block_open(out);
+ out << indent() << "return [";
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ bool wrote = false;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ wrote = true;
+ out << "\"" << (*f_iter)->get_name() << "\": " << (*f_iter)->get_key() << ", ";
+ }
+ if (!wrote) {
+ // pad a colon
+ out << ":";
+ }
+ out << "]" << endl;
+ block_close(out);
+ out << endl;
+ out << indent() << access << " static var structName: String { return \""
+ << tstruct->get_name() << "\" }" << endl << endl;
+
+ if (tstruct->is_union()) {
+ generate_swift_union_reader(out, tstruct);
+ } else {
+ generate_swift_struct_reader(out, tstruct, is_private);
+ }
+ } else {
+ /** Legacy Swift2/Cocoa */
+
+ generate_swift_struct_reader(out, tstruct, is_private);
+
+ if (is_result) {
+ generate_old_swift_struct_result_writer(out, tstruct);
+ }
+ else {
+ generate_old_swift_struct_writer(out, tstruct, is_private);
+ }
+ }
+
+ block_close(out);
+ out << endl;
+}
+
+void t_swift_generator::generate_swift_union_reader(ostream& out, t_struct* tstruct) {
+ indent(out) << "public static func read(from proto: TProtocol) throws -> "
+ << tstruct->get_name();
+ block_open(out);
+ indent(out) << "_ = try proto.readStructBegin()" << endl;
+
+ indent(out) << "var ret: " << tstruct->get_name() << "?";
+ out << endl;
+ indent(out) << "fields: while true";
+ block_open(out);
+ out << endl;
+ indent(out) << "let (_, fieldType, fieldID) = try proto.readFieldBegin()" << endl << endl;
+ indent(out) << "switch (fieldID, fieldType)";
+ block_open(out);
+ indent(out) << "case (_, .stop): break fields" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case (" << (*f_iter)->get_key() << ", " << type_to_enum((*f_iter)->get_type()) << "):";
+ string padding = "";
+
+ t_type* type = get_true_type((*f_iter)->get_type());
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ case t_base_type::TYPE_DOUBLE:
+ padding = " ";
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ padding = " ";
+ break;
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ padding = " ";
+ break;
+ default: break;
+ }
+ } else if (type->is_enum() || type->is_set() || type->is_map()) {
+ padding = " ";
+ } else if (type->is_struct() || type->is_xception()) {
+ padding = " ";
+ } else if (type->is_list()) {
+ padding = " ";
+ }
+
+ indent(out) << padding << "ret = " << tstruct->get_name() << "."
+ << (*f_iter)->get_name() << "(val: " << "try "
+ << type_name((*f_iter)->get_type(), false, false)
+ << ".read(from: proto))" << endl;
+ }
+
+ indent(out) << "case let (_, unknownType): try proto.skip(type: unknownType)" << endl;
+
+ block_close(out);
+ indent(out) << "try proto.readFieldEnd()" << endl;
+
+ block_close(out);
+ out << endl;
+
+ indent(out) << "try proto.readStructEnd()" << endl;
+
+ indent(out) << "if let ret = ret";
+ block_open(out);
+ indent(out) << "return ret" << endl;
+ block_close(out);
+ out << endl;
+ indent(out) << "throw TProtocolError(error: .unknown, message: \"Missing required value for type: "
+ << tstruct->get_name() << "\")";
+ block_close(out);
+ out << endl;
+
+}
+
+/**
+ * Generates a function to read a struct from
+ * from a protocol. (TStruct compliance)
+ *
+ * @param tstruct The structure definition
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_swift_struct_reader(ostream& out,
+ t_struct* tstruct,
+ bool is_private) {
+
+ if (!gen_cocoa_) {
+ /** Swift 3 case */
+ string visibility = is_private ? "fileprivate" : "public";
+
+ indent(out) << visibility << " static func read(from proto: TProtocol) throws -> "
+ << tstruct->get_name();
+
+ block_open(out);
+ indent(out) << "_ = try proto.readStructBegin()" << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool optional = field_is_optional(*f_iter);
+ indent(out) << "var " << maybe_escape_identifier((*f_iter)->get_name()) << ": "
+ << type_name((*f_iter)->get_type(), optional, !optional) << endl;
+ }
+
+ out << endl;
+
+ // Loop over reading in fields
+ indent(out) << "fields: while true";
+ block_open(out);
+ out << endl;
+
+ indent(out) << "let (_, fieldType, fieldID) = try proto.readFieldBegin()" << endl << endl;
+ indent(out) << "switch (fieldID, fieldType)";
+ block_open(out);
+ indent(out) << "case (_, .stop): break fields" << endl;
+
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "case (" << (*f_iter)->get_key() << ", " << type_to_enum((*f_iter)->get_type()) << "):";
+ string padding = "";
+
+ t_type* type = get_true_type((*f_iter)->get_type());
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ case t_base_type::TYPE_DOUBLE:
+ padding = " ";
+ break;
+
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ padding = " ";
+ break;
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ padding = " ";
+ break;
+ default: break;
+ }
+ } else if (type->is_enum() || type->is_set() || type->is_map()) {
+ padding = " ";
+ } else if (type->is_struct() || type->is_xception()) {
+ padding = " ";
+ } else if (type->is_list()) {
+ padding = " ";
+ }
+
+ out << padding << maybe_escape_identifier((*f_iter)->get_name()) << " = try "
+ << type_name((*f_iter)->get_type(), false, false) << ".read(from: proto)" << endl;
+ }
+
+ indent(out) << "case let (_, unknownType): try proto.skip(type: unknownType)" << endl;
+ block_close(out);
+ out << endl;
+
+ // Read field end marker
+ indent(out) << "try proto.readFieldEnd()" << endl;
+ block_close(out);
+ out << endl;
+ indent(out) << "try proto.readStructEnd()" << endl;
+
+ if (struct_has_required_fields(tstruct)) {
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "// Required fields" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (field_is_optional(*f_iter)) {
+ continue;
+ }
+ indent(out) << "try proto.validateValue(" << (*f_iter)->get_name() << ", "
+ << "named: \"" << (*f_iter)->get_name() << "\")" << endl;
+ }
+ }
+
+ out << endl;
+
+ indent(out) << "return " << tstruct->get_name() << "(";
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ out << (*f_iter)->get_name() << ": " << maybe_escape_identifier((*f_iter)->get_name());
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+
+ } else {
+ /** Legacy Swif2/Cocoa case */
+ string visibility = is_private ? "private" : "public";
+
+ indent(out) << visibility << " static func readValueFromProtocol(__proto: TProtocol) throws -> "
+ << tstruct->get_name();
+
+ block_open(out);
+ out << endl;
+ indent(out) << "try __proto.readStructBegin()" << endl << endl;
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ bool optional = field_is_optional(*f_iter);
+ indent(out) << "var " << maybe_escape_identifier((*f_iter)->get_name()) << " : "
+ << type_name((*f_iter)->get_type(), optional, !optional) << endl;
+ }
+
+ out << endl;
+
+ // Loop over reading in fields
+ indent(out) << "fields: while true";
+ block_open(out);
+ out << endl;
+
+ indent(out) << "let (_, fieldType, fieldID) = try __proto.readFieldBegin()" << endl << endl;
+ indent(out) << "switch (fieldID, fieldType)";
+
+ block_open(out);
+
+ indent(out) << "case (_, .STOP):" << endl;
+ indent_up();
+ indent(out) << "break fields" << endl << endl;
+ indent_down();
+
+ // Generate deserialization code for known cases
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+
+ indent(out) << "case (" << (*f_iter)->get_key() << ", " << type_to_enum((*f_iter)->get_type()) << "):" << endl;
+ indent_up();
+ indent(out) << maybe_escape_identifier((*f_iter)->get_name()) << " = try __proto.readValue() as "
+ << type_name((*f_iter)->get_type()) << endl << endl;
+ indent_down();
+
+ }
+
+ indent(out) << "case let (_, unknownType):" << endl;
+ indent_up();
+ indent(out) << "try __proto.skipType(unknownType)" << endl;
+ indent_down();
+ block_close(out);
+ out << endl;
+
+ // Read field end marker
+ indent(out) << "try __proto.readFieldEnd()" << endl;
+
+ block_close(out);
+ out << endl;
+ indent(out) << "try __proto.readStructEnd()" << endl;
+ out << endl;
+
+ if (struct_has_required_fields(tstruct)) {
+ // performs various checks (e.g. check that all required fields are set)
+ indent(out) << "// Required fields" << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (field_is_optional(*f_iter)) {
+ continue;
+ }
+ indent(out) << "try __proto.validateValue(" << (*f_iter)->get_name() << ", "
+ << "named: \"" << (*f_iter)->get_name() << "\")" << endl;
+ }
+ }
+
+ out << endl;
+
+ indent(out) << "return " << tstruct->get_name() << "(";
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ out << (*f_iter)->get_name() << ": " << maybe_escape_identifier((*f_iter)->get_name());
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+ }
+ out << ")" << endl;
+
+ block_close(out);
+
+ out << endl;
+}
+
+/**
+ * Generates a function to write a struct to
+ * a protocol. (TStruct compliance) ONLY FOR SWIFT2/COCOA
+ *
+ * @param tstruct The structure definition
+ * @param is_private
+ * Is the struct public or private
+ */
+void t_swift_generator::generate_old_swift_struct_writer(ostream& out,
+ t_struct* tstruct,
+ bool is_private) {
+
+ string visibility = is_private ? "private" : "public";
+
+ indent(out) << visibility << " static func writeValue(__value: " << tstruct->get_name()
+ << ", toProtocol __proto: TProtocol) throws";
+ block_open(out);
+ out << endl;
+
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "try __proto.writeStructBeginWithName(\"" << name << "\")" << endl;
+ out << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field *tfield = *f_iter;
+
+ bool optional = field_is_optional(tfield);
+ if (optional) {
+ indent(out) << "if let " << maybe_escape_identifier(tfield->get_name())
+ << " = __value." << maybe_escape_identifier(tfield->get_name());
+ block_open(out);
+ }
+
+ indent(out) << "try __proto.writeFieldValue("
+ << (optional ? "" : "__value.") << maybe_escape_identifier(tfield->get_name()) << ", "
+ << "name: \"" << tfield->get_name() << "\", "
+ << "type: " << type_to_enum(tfield->get_type()) << ", "
+ << "id: " << tfield->get_key() << ")" << endl;
+
+ if (optional) {
+ block_close(out);
+ }
+
+ out << endl;
+ }
+
+ indent(out) << "try __proto.writeFieldStop()" << endl << endl;
+ indent(out) << "try __proto.writeStructEnd()" << endl;
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a function to read a struct from
+ * from a protocol. (TStruct compliance) ONLY FOR SWIFT 2/COCOA
+ *
+ * This is specifically a function result. Only
+ * the first available field is written.
+ *
+ * @param tstruct The structure definition
+ */
+void t_swift_generator::generate_old_swift_struct_result_writer(ostream& out, t_struct* tstruct) {
+
+ indent(out) << "private static func writeValue(__value: " << tstruct->get_name()
+ << ", toProtocol __proto: TProtocol) throws";
+ block_open(out);
+ out << endl;
+ string name = tstruct->get_name();
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ indent(out) << "try __proto.writeStructBeginWithName(\"" << name << "\")" << endl;
+ out << endl;
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field *tfield = *f_iter;
+
+ indent(out) << "if let result = __value." << (*f_iter)->get_name();
+
+ block_open(out);
+
+ indent(out) << "try __proto.writeFieldValue(result, "
+ << "name: \"" << tfield->get_name() << "\", "
+ << "type: " << type_to_enum(tfield->get_type()) << ", "
+ << "id: " << tfield->get_key() << ")" << endl;
+
+ block_close(out);
+ }
+ // Write the struct map
+ indent(out) << "try __proto.writeFieldStop()" << endl << endl;
+ indent(out) << "try __proto.writeStructEnd()" << endl;
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a description method for the given struct
+ *
+ * @param tstruct The struct definition
+ */
+void t_swift_generator::generate_swift_struct_printable_extension(ostream& out, t_struct* tstruct) {
+
+ // Allow use of debugDescription so the app can add description via a cateogory/extension
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ indent(out) << "extension " << tstruct->get_name() << " : "
+ << (debug_descriptions_ ? "CustomDebugStringConvertible" : "CustomStringConvertible");
+
+ block_open(out);
+ out << endl;
+ indent(out) << "public var description : String";
+ block_open(out);
+ indent(out) << "var desc = \"" << tstruct->get_name();
+
+ if (!gen_cocoa_) {
+ if (!tstruct->is_union()) {
+ out << "(\"" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ indent(out) << "desc += \"" << (*f_iter)->get_name()
+ << "=\\(String(describing: self." << maybe_escape_identifier((*f_iter)->get_name()) << "))";
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ out << "\"" << endl;
+ }
+ } else {
+ out << ".\"" << endl;
+ indent(out) << "switch self {" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end();f_iter++) {
+ indent(out) << "case ." << (*f_iter)->get_name() << "(let val): "
+ << "desc += \"" << (*f_iter)->get_name() << "(val: \\(val))\""
+ << endl;
+ }
+ indent(out) << "}" << endl;
+ }
+ } else {
+ out << "(\"" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ indent(out) << "desc += \"" << (*f_iter)->get_name()
+ << "=\\(self." << maybe_escape_identifier((*f_iter)->get_name()) << ")";
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ out << "\"" << endl;
+ }
+ indent(out) << "desc += \")\"" << endl;
+ }
+
+ indent(out) << "return desc" << endl;
+ block_close(out);
+ out << endl;
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a thrift service. In Swift this consists of a
+ * protocol definition and a client (with it's implementation
+ * separated into exts file).
+ *
+ * @param tservice The service definition
+ */
+void t_swift_generator::generate_service(t_service* tservice) {
+
+ generate_swift_service_protocol(f_decl_, tservice);
+ generate_swift_service_client(f_decl_, tservice);
+ if (async_clients_) {
+ generate_swift_service_protocol_async(f_decl_, tservice);
+ generate_swift_service_client_async(f_decl_, tservice);
+ }
+ generate_swift_service_server(f_decl_, tservice);
+
+ generate_swift_service_helpers(tservice);
+
+ generate_swift_service_client_implementation(f_impl_, tservice);
+ if (async_clients_) {
+ generate_swift_service_client_async_implementation(f_impl_, tservice);
+ }
+ generate_swift_service_server_implementation(f_impl_, tservice);
+}
+
+/**
+ * Generates structs for all the service return types
+ *
+ * @param tservice The service
+ */
+void t_swift_generator::generate_swift_service_helpers(t_service* tservice) {
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+
+ t_struct* ts = (*f_iter)->get_arglist();
+
+ string qname = function_args_helper_struct_type(tservice, *f_iter);
+
+ t_struct qname_ts = t_struct(ts->get_program(), qname);
+
+ const vector<t_field*>& members = ts->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ qname_ts.append(*m_iter);
+ }
+
+ generate_swift_struct(f_impl_, &qname_ts, true);
+ generate_swift_struct_implementation(f_impl_, &qname_ts, false, true);
+ generate_function_helpers(tservice, *f_iter);
+ }
+}
+
+string t_swift_generator::function_result_helper_struct_type(t_service *tservice, t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return tservice->get_name() + "_" + tfunction->get_name();
+ } else {
+ return tservice->get_name() + "_" + tfunction->get_name() + "_result";
+ }
+}
+
+string t_swift_generator::function_args_helper_struct_type(t_service *tservice, t_function* tfunction) {
+ return tservice->get_name() + "_" + tfunction->get_name() + "_args";
+}
+
+/**
+ * Generates a struct and helpers for a function.
+ *
+ * @param tfunction The function
+ */
+void t_swift_generator::generate_function_helpers(t_service *tservice, t_function* tfunction) {
+ if (tfunction->is_oneway()) {
+ return;
+ }
+
+ // create a result struct with a success field of the return type,
+ // and a field for each type of exception thrown
+ t_struct result(program_, function_result_helper_struct_type(tservice, tfunction));
+ if (!tfunction->get_returntype()->is_void()) {
+ t_field* success = new t_field(tfunction->get_returntype(), "success", 0);
+ success->set_req(t_field::T_OPTIONAL);
+ result.append(success);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& fields = xs->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ t_field *x = *f_iter;
+ t_field *ox = new t_field(x->get_type(), x->get_name(), x->get_key());
+ ox->set_req(t_field::T_OPTIONAL);
+ result.append(ox);
+ }
+
+ // generate the result struct
+ generate_swift_struct(f_impl_, &result, true);
+ generate_swift_struct_implementation(f_impl_, &result, true, true);
+
+ for (f_iter = result.get_members().begin(); f_iter != result.get_members().end(); ++f_iter) {
+ delete *f_iter;
+ }
+}
+
+/**
+ * Generates a service protocol definition.
+ *
+ * @param tservice The service to generate a protocol definition for
+ */
+void t_swift_generator::generate_swift_service_protocol(ostream& out, t_service* tservice) {
+ if (!gen_cocoa_) {
+ string doc = tservice->get_doc();
+ generate_docstring(out, doc);
+
+ indent(out) << "public protocol " << tservice->get_name();
+ t_service* parent = tservice->get_extends();
+ if (parent != NULL) {
+ out << " : " << parent->get_name();
+ }
+ block_open(out);
+ out << endl;
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ function_docstring(out, *f_iter);
+ indent(out) << function_signature(*f_iter) << endl << endl;
+ }
+
+ } else {
+ indent(out) << "public protocol " << tservice->get_name();
+ block_open(out);
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ out << endl;
+ indent(out) << function_signature(*f_iter) << " // exceptions: ";
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << type_name((*x_iter)->get_type()) + ", ";
+ }
+ out << endl;
+ }
+ }
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates an asynchronous service protocol definition.
+ *
+ * @param tservice The service to generate a protocol definition for
+ */
+void t_swift_generator::generate_swift_service_protocol_async(ostream& out, t_service* tservice) {
+ if (!gen_cocoa_) {
+ string doc = tservice->get_doc();
+ generate_docstring(out, doc);
+ }
+ indent(out) << "public protocol " << tservice->get_name() << "Async";
+
+ block_open(out);
+ if (!gen_cocoa_) { out << endl; }
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ if (!gen_cocoa_) {
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ async_function_docstring(out, *f_iter);
+ indent(out) << async_function_signature(*f_iter) << endl << endl;
+ }
+ } else {
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ out << endl;
+ indent(out) << async_function_signature(*f_iter) << endl;
+ if (promise_kit_) {
+ indent(out) << promise_function_signature(*f_iter) << endl;
+ } //
+ out << endl;
+ }
+ }
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a service client interface definition.
+ *
+ * @param tservice The service to generate a client interface definition for
+ */
+void t_swift_generator::generate_swift_service_client(ostream& out, t_service* tservice) {
+ if (!gen_cocoa_) {
+ indent(out) << "open class " << tservice->get_name() << "Client";// : "
+
+ // Inherit from ParentClient
+ t_service* parent = tservice->get_extends();
+ out << " : " << ((parent == NULL) ? "TClient" : parent->get_name() + "Client");
+ out << " /* , " << tservice->get_name() << " */";
+ block_open(out);
+ out << endl;
+ } else {
+ // a
+ indent(out) << "public class " << tservice->get_name() << "Client /* : " << tservice->get_name() << " */";
+ block_open(out);
+ out << endl;
+
+ indent(out) << "let __inProtocol : TProtocol" << endl << endl;
+ indent(out) << "let __outProtocol : TProtocol" << endl << endl;
+ indent(out) << "public init(inoutProtocol: TProtocol)";
+ block_open(out);
+
+ indent(out) << "__inProtocol = inoutProtocol" << endl;
+ indent(out) << "__outProtocol = inoutProtocol" << endl;
+ block_close(out);
+ out << endl;
+
+ indent(out) << "public init(inProtocol: TProtocol, outProtocol: TProtocol)";
+ block_open(out);
+ indent(out) << "__inProtocol = inProtocol" << endl;
+ indent(out) << "__outProtocol = outProtocol" << endl;
+ block_close(out);
+ out << endl;
+ }
+
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a service client interface definition.
+ *
+ * @param tservice The service to generate a client interface definition for
+ */
+void t_swift_generator::generate_swift_service_client_async(ostream& out, t_service* tservice) {
+ if (!gen_cocoa_) {
+ indent(out) << "open class " << tservice->get_name()
+ << "AsyncClient<Protocol: TProtocol, Factory: TAsyncTransportFactory>";// : "
+
+ // Inherit from ParentClient
+ t_service* parent = tservice->get_extends();
+
+ out << " : " << ((parent == NULL) ? "T" : parent->get_name()) + "AsyncClient<Protocol, Factory>";
+ out << " /* , " << tservice->get_name() << " */";
+
+ block_open(out);
+ out << endl;
+ } else {
+ indent(out) << "public class " << tservice->get_name() << "AsyncClient /* : " << tservice->get_name() << " */";
+ block_open(out);
+ out << endl;
+
+ indent(out) << "let __protocolFactory : TProtocolFactory" << endl << endl;
+ indent(out) << "let __transportFactory : TAsyncTransportFactory" << endl << endl;
+ indent(out) << "public init(protocolFactory: TProtocolFactory, transportFactory: TAsyncTransportFactory)";
+ block_open(out);
+
+ indent(out) << "__protocolFactory = protocolFactory" << endl;
+ indent(out) << "__transportFactory = transportFactory" << endl;
+ block_close(out);
+ out << endl;
+ }
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a service server interface definition. In other words,
+ * the TProcess implementation for the service definition.
+ *
+ * @param tservice The service to generate a client interface definition for
+ */
+void t_swift_generator::generate_swift_service_server(ostream& out, t_service* tservice) {
+ if (!gen_cocoa_) {
+ indent(out) << "open class " << tservice->get_name() << "Processor /* " << tservice->get_name() << " */";
+
+ block_open(out);
+ out << endl;
+ out << indent() << "typealias ProcessorHandlerDictionary = "
+ << "[String: (Int32, TProtocol, TProtocol, " << tservice->get_name() << ") throws -> Void]" << endl
+ << endl
+ << indent() << "public var service: " << tservice->get_name() << endl
+ << endl
+ << indent() << "public required init(service: " << tservice->get_name() << ")";
+ } else {
+ indent(out) << "public class " << tservice->get_name() << "Processor : NSObject /* "
+ << tservice->get_name() << " */";
+ block_open(out);
+ out << endl;
+
+ out << indent() << "typealias ProcessorHandlerDictionary = "
+ << "[String: (Int, TProtocol, TProtocol, " << tservice->get_name() << ") throws -> Void]" << endl
+ << endl
+ << indent() << "let service : " << tservice->get_name() << endl
+ << endl
+ << indent() << "public init(service: " << tservice->get_name() << ")";
+ }
+
+ block_open(out);
+ indent(out) << "self.service = service" << endl;
+ block_close(out);
+ out << endl;
+
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a function that will send the arguments
+ * for a service function via a protocol.
+ *
+ * @param tservice The service to generate
+ * @param tfunction The function to generate
+ * @param needs_protocol
+ * Wether the first parameter must be a protocol or if
+ * the protocol is to be assumed
+ */
+void t_swift_generator::generate_swift_service_client_send_function_implementation(ostream& out,
+ t_service *tservice,
+ t_function* tfunction,
+ bool needs_protocol) {
+
+ string funname = tfunction->get_name();
+
+ t_function send_function(g_type_bool,
+ "send_" + tfunction->get_name(),
+ tfunction->get_arglist());
+
+ string argsname = function_args_helper_struct_type(tservice, tfunction);
+ t_struct* arg_struct = tfunction->get_arglist();
+
+ string proto = needs_protocol ? (gen_cocoa_ ? "__outProtocol" : "on outProtocol") : "";
+ // Open function
+ indent(out) << "private func " << send_function.get_name() << "("
+ << argument_list(tfunction->get_arglist(), proto, true)
+ << ") throws";
+ block_open(out);
+ if (!gen_cocoa_) {
+ // Serialize the request
+ indent(out) << "try outProtocol.writeMessageBegin(name: \"" << funname << "\", "
+ << "type: " << (tfunction->is_oneway() ? ".oneway" : ".call") << ", "
+ << "sequenceID: 0)" << endl;
+
+ indent(out) << "let args = " << argsname << "(";
+
+ // write out function parameters
+
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ t_field *tfield = (*f_iter);
+ out << tfield->get_name() << ": " << tfield->get_name();
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+ out << ")" << endl;
+ indent(out) << "try args.write(to: outProtocol)" << endl;
+ indent(out) << "try outProtocol.writeMessageEnd()" << endl;
+ } else {
+ out << endl;
+
+ // Serialize the request
+ indent(out) << "try __outProtocol.writeMessageBeginWithName(\"" << funname << "\", "
+ << "type: " << (tfunction->is_oneway() ? ".ONEWAY" : ".CALL") << ", "
+ << "sequenceID: 0)" << endl;
+
+ out << endl;
+
+ indent(out) << "let __args = " << argsname << "(";
+
+ // write out function parameters
+
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ t_field *tfield = (*f_iter);
+ out << tfield->get_name() << ": " << tfield->get_name();
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+ out << ")" << endl;
+ indent(out) << "try " << argsname << ".writeValue(__args, toProtocol: __outProtocol)" << endl << endl;
+ indent(out) << "try __outProtocol.writeMessageEnd()" << endl;
+ }
+
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a function that will recv the result for a
+ * service function via a protocol.
+ *
+ * @param tservice The service to generate
+ * @param tfunction The function to generate
+ * @param needs_protocol
+ * Wether the first parameter must be a protocol or if
+ * the protocol is to be assumed
+ */
+void t_swift_generator::generate_swift_service_client_recv_function_implementation(ostream& out,
+ t_service* tservice,
+ t_function* tfunction,
+ bool needs_protocol) {
+
+ // Open function
+ indent(out) << "private func recv_" << tfunction->get_name() << "(";
+ if (!gen_cocoa_) {
+ if (needs_protocol) {
+ out << "on inProtocol: TProtocol";
+ }
+ out << ") throws";
+ if (!tfunction->get_returntype()->is_void()) {
+ out << " -> " << type_name(tfunction->get_returntype());
+ }
+
+ block_open(out);
+
+ // check for an exception
+
+ indent(out) << "try inProtocol.readResultMessageBegin() " << endl;
+
+ string resultname = function_result_helper_struct_type(tservice, tfunction);
+ indent(out);
+ if (!tfunction->get_returntype()->is_void() || !tfunction->get_xceptions()->get_members().empty()) {
+ out << "let result = ";
+ } else {
+ out << "_ = ";
+ }
+
+ string return_type_name = type_name(tfunction->get_returntype());
+ out << "try " << resultname << ".read(from: inProtocol)" << endl;
+
+ indent(out) << "try inProtocol.readMessageEnd()" << endl << endl;
+
+ // Careful, only return _result if not a void function
+ if (!tfunction->get_returntype()->is_void()) {
+ indent(out) << "if let success = result.success";
+ block_open(out);
+ indent(out) << "return success" << endl;
+ block_close(out);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent(out) << "if let " << (*x_iter)->get_name() << " = result." << (*x_iter)->get_name();
+ block_open(out);
+ indent(out) << "throw " << (*x_iter)->get_name() << endl;
+ block_close(out);
+ }
+
+ // If you get here it's an exception, unless a void function
+ if (!tfunction->get_returntype()->is_void()) {
+ indent(out) << "throw TApplicationError(error: .missingResult(methodName: \""
+ << tfunction->get_name() << "\"))" << endl;
+ }
+ } else {
+ if (needs_protocol) {
+ out << "__inProtocol: TProtocol";
+ }
+
+ out << ") throws";
+
+ if (!tfunction->get_returntype()->is_void()) {
+ out << " -> " << type_name(tfunction->get_returntype());
+ }
+
+ block_open(out);
+
+ // check for an exception
+ out << endl;
+ indent(out) << "try __inProtocol.readResultMessageBegin() " << endl << endl;
+ string resultname = function_result_helper_struct_type(tservice, tfunction);
+ indent(out);
+ if (!tfunction->get_returntype()->is_void() || !tfunction->get_xceptions()->get_members().empty()) {
+ out << "let __result = ";
+ }
+ out << "try " << resultname << ".readValueFromProtocol(__inProtocol)" << endl << endl;
+
+ indent(out) << "try __inProtocol.readMessageEnd()" << endl << endl;
+
+ // Careful, only return _result if not a void function
+ if (!tfunction->get_returntype()->is_void()) {
+ indent(out) << "if let __success = __result.success";
+ block_open(out);
+ indent(out) << "return __success" << endl;
+ block_close(out);
+ }
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ indent(out) << "if let " << (*x_iter)->get_name() << " = __result." << (*x_iter)->get_name();
+ block_open(out);
+ indent(out) << "throw " << (*x_iter)->get_name() << endl;
+ block_close(out);
+ }
+
+ // If you get here it's an exception, unless a void function
+ if (!tfunction->get_returntype()->is_void()) {
+ indent(out) << "throw NSError(" << endl;
+ indent_up();
+ indent(out) << "domain: TApplicationErrorDomain, " << endl;
+ indent(out) << "code: Int(TApplicationError.MissingResult.rawValue)," << endl;
+ indent(out) << "userInfo: [TApplicationErrorMethodKey: \"" << tfunction->get_name() << "\"])" << endl;
+ indent_down();
+ }
+ }
+
+ // Close function
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates an invocation of a given the send function for the
+ * service function.
+ *
+ * @param tfunction The service to generate an implementation for
+ */
+void t_swift_generator::generate_swift_service_client_send_function_invocation(ostream& out,
+ t_function* tfunction) {
+
+ indent(out) << "try send_" << tfunction->get_name() << "(";
+
+ t_struct* arg_struct = tfunction->get_arglist();
+
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ out << (*f_iter)->get_name() << ": " << (*f_iter)->get_name();
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+
+ out << ")" << endl;
+}
+
+/**
+ * Generates an invocation of a given the send function for the
+ * service function. This is for asynchronous protocols.
+ *
+ * @param tfunction The service to generate an implementation for
+ */
+void t_swift_generator::generate_swift_service_client_send_async_function_invocation(ostream& out,
+ t_function* tfunction) {
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (!gen_cocoa_) {
+ indent(out) << "try send_" << tfunction->get_name() << "(on: proto";
+ } else {
+ indent(out) << "try send_" << tfunction->get_name() << "(__protocol"; //
+ }
+
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ out << ", " << (*f_iter)->get_name() << ": " << (*f_iter)->get_name();
+ }
+
+ out << ")" << endl;
+}
+
+/**
+ * Generates a service client protocol implementation via extension.
+ *
+ * @param tservice The service to generate an implementation for
+ */
+void t_swift_generator::generate_swift_service_client_implementation(ostream& out,
+ t_service* tservice) {
+
+ string name = tservice->get_name() + "Client";
+ indent(out) << "extension " << name << " : " << tservice->get_name();
+ block_open(out);
+ out << endl;
+
+ // generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+
+ generate_swift_service_client_send_function_implementation(out, tservice, *f_iter, false);
+
+ if (!(*f_iter)->is_oneway()) {
+ generate_swift_service_client_recv_function_implementation(out, tservice, *f_iter, false);
+ }
+
+ // Open function
+ indent(out) << "public " << function_signature(*f_iter);
+ block_open(out);
+
+ if (gen_cocoa_) { out << endl; }
+
+ generate_swift_service_client_send_function_invocation(out, *f_iter);
+ if (!gen_cocoa_) {
+ indent(out) << "try outProtocol.transport.flush()" << endl;
+ } else {
+ out << endl;
+ indent(out) << "try __outProtocol.transport().flush()" << endl << endl;
+ }
+
+ if (!(*f_iter)->is_oneway()) {
+ if ((*f_iter)->get_returntype()->is_void()) {
+ indent(out) << "try recv_" << (*f_iter)->get_name() << "()" << endl;
+ } else {
+ indent(out) << "return try recv_" << (*f_iter)->get_name() << "()" << endl;
+ }
+ }
+ block_close(out);
+ out << endl;
+ }
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a service asynchronous client protocol implementation via extension.
+ *
+ * @param tservice The service to generate an implementation for
+ */
+void t_swift_generator::generate_swift_service_client_async_implementation(ostream& out, t_service* tservice) {
+ if (gen_cocoa_) {
+ generate_old_swift_service_client_async_implementation(out, tservice);
+ return;
+ }
+ string name = tservice->get_name() + "AsyncClient";
+ string protocol_name = tservice->get_name() + "Async";
+
+ indent(out) << "extension " << name << " : " << protocol_name;
+ block_open(out);
+ out << endl;
+
+ // generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+
+ generate_swift_service_client_send_function_implementation(out, tservice, *f_iter, true);
+
+ if (!(*f_iter)->is_oneway()) {
+ generate_swift_service_client_recv_function_implementation(out, tservice, *f_iter, true);
+ }
+
+ indent(out) << "public " << async_function_signature(*f_iter);
+ block_open(out);
+ out << endl;
+ out << indent() << "let transport = factory.newTransport()" << endl
+ << indent() << "let proto = Protocol(on: transport)" << endl
+ << endl;
+
+ out << indent() << "do";
+ block_open(out);
+
+ generate_swift_service_client_send_async_function_invocation(out, *f_iter);
+
+ indent_down();
+ out << indent() << "} catch let error {" << endl;
+ indent_up();
+ out << indent() << "completion(.error(error))" << endl;
+ block_close(out);
+
+ out << endl;
+
+ bool ret_is_void = (*f_iter)->get_returntype()->is_void();
+ bool is_oneway = (*f_iter)->is_oneway();
+
+ string error_completion_call = "completion(.error(error))";
+ indent(out) << "transport.flush";
+ block_open(out);
+ out << indent() << "(trans, error) in" << endl << endl;
+ out << indent() << "if let error = error";
+ block_open(out);
+ out << indent() << error_completion_call << endl;
+ block_close(out);
+
+ if (!is_oneway) {
+ out << indent() << "do";
+ block_open(out);
+ indent(out);
+ if (!ret_is_void) {
+ out << "let result = ";
+ }
+ out << "try self.recv_" << (*f_iter)->get_name() << "(on: proto)" << endl;
+
+ out << indent() << (ret_is_void ? "completion(.success(Void()))" : "completion(.success(result))") << endl;
+ indent_down();
+ out << indent() << "} catch let error {" << endl;
+ indent_up();
+ out << indent() << error_completion_call << endl;
+
+ block_close(out);
+ } else {
+ out << indent() << "completion(.success(Void()))" << endl;
+ }
+ block_close(out);
+ block_close(out);
+
+ }
+ block_close(out);
+ out << endl;
+}
+
+void t_swift_generator::generate_old_swift_service_client_async_implementation(ostream& out,
+ t_service* tservice) {
+
+ string name = tservice->get_name() + "AsyncClient";
+ string protocol_name = tservice->get_name() + "Async";
+
+ indent(out) << "extension " << name << " : " << protocol_name;
+ block_open(out);
+ out << endl;
+
+ // generate client method implementations
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+
+ generate_swift_service_client_send_function_implementation(out, tservice, *f_iter, true);
+
+ if (!(*f_iter)->is_oneway()) {
+ generate_swift_service_client_recv_function_implementation(out, tservice, *f_iter, true);
+ }
+
+ indent(out) << "public " << async_function_signature(*f_iter);
+ block_open(out);
+ out << endl;
+
+ out << indent() << "let __transport = __transportFactory.newTransport()" << endl
+ << indent() << "let __protocol = __protocolFactory.newProtocolOnTransport(__transport)" << endl
+ << endl;
+
+ generate_swift_service_client_send_async_function_invocation(out, *f_iter);
+ out << endl;
+
+ indent(out) << "__transport.flushWithCompletion(";
+
+ if ((*f_iter)->is_oneway()) {
+ out << "success, failure: failure)" << endl;
+ }
+ else {
+ block_open(out);
+ indent(out) << "do";
+ block_open(out);
+
+ indent(out);
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << "let result = ";
+ }
+ out << "try self.recv_" << (*f_iter)->get_name() << "(__protocol)" << endl;
+
+ out << indent() << "success(";
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << "result";
+ }
+ out << ")" << endl;
+
+ block_close(out);
+ indent(out) << "catch let error";
+ block_open(out);
+ indent(out) << "failure(error as NSError)" << endl;
+ block_close(out);
+ block_close(out);
+ indent(out) << ", failure: failure)" << endl;
+ }
+
+ block_close(out);
+ out << endl;
+
+ // Promise function
+ if (promise_kit_) {
+
+ indent(out) << "public " << promise_function_signature(*f_iter);
+ block_open(out);
+
+ out << indent() << "let (__promise, __fulfill, __reject) = Promise<" << type_name((*f_iter)->get_returntype()) << ">.pendingPromise()" << endl << endl
+ << indent() << "let __transport = __transportFactory.newTransport()" << endl
+ << indent() << "let __protocol = __protocolFactory.newProtocolOnTransport(__transport)" << endl
+ << endl;
+
+ generate_swift_service_client_send_async_function_invocation(out, *f_iter);
+ out << endl;
+ indent(out) << "__transport.flushWithCompletion(";
+
+ if ((*f_iter)->is_oneway()) {
+ out << "{ __fulfill() }, failure: { __reject($0) })" << endl;
+ }
+ else {
+ block_open(out);
+ indent(out) << "do";
+ block_open(out);
+
+ indent(out);
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << "let result = ";
+ }
+ out << "try self.recv_" << (*f_iter)->get_name() << "(__protocol)" << endl;
+
+ out << indent() << "__fulfill(";
+ if (!(*f_iter)->get_returntype()->is_void()) {
+ out << "result";
+ }
+ out << ")" << endl;
+
+ block_close(out);
+ indent(out) << "catch let error";
+ block_open(out);
+ indent(out) << "__reject(error)" << endl;
+ block_close(out);
+ block_close(out);
+
+ indent(out) << ", failure: { error in " << endl;
+ indent_up();
+ indent(out) << "__reject(error)" << endl;
+ indent_down();
+ indent(out) << "})" << endl;
+ }
+
+ indent(out) << "return __promise" << endl;
+ block_close(out);
+ out << endl;
+
+ }
+
+ }
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Generates a service server implementation.
+ *
+ * Implemented by generating a block for each service function that
+ * handles the processing of that function. The blocks are stored in
+ * a map and looked up via function/message name.
+ *
+ * @param tservice The service to generate an implementation for
+ */
+void t_swift_generator::generate_swift_service_server_implementation(ostream& out,
+ t_service* tservice) {
+
+ string name = tservice->get_name() + "Processor";
+
+ indent(out) << "extension " << name << " : TProcessor";
+ block_open(out);
+ out << endl;
+ indent(out) << "static let processorHandlers" << (gen_cocoa_ ? " " : "") << ": ProcessorHandlerDictionary =";
+ block_open(out);
+
+ out << endl;
+ out << indent() << "var processorHandlers = ProcessorHandlerDictionary()" << endl << endl;
+
+ // generate method map for routing incoming calls
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::const_iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+
+ t_function* tfunction = *f_iter;
+
+ string args_type = function_args_helper_struct_type(tservice, *f_iter);
+
+ out << indent() << "processorHandlers[\"" << tfunction->get_name() << "\"] = { sequenceID, inProtocol, outProtocol, handler in" << endl
+ << endl;
+
+ indent_up();
+ if (!gen_cocoa_) {
+ out << indent() << "let args = try " << args_type << ".read(from: inProtocol)" << endl
+ << endl
+ << indent() << "try inProtocol.readMessageEnd()" << endl
+ << endl;
+ } else {
+ out << indent() << "let args = try " << args_type << ".readValueFromProtocol(inProtocol)" << endl
+ << endl
+ << indent() << "try inProtocol.readMessageEnd()" << endl
+ << endl;
+ }
+
+ if (!tfunction->is_oneway() ) {
+ string result_type = function_result_helper_struct_type(tservice, tfunction);
+ indent(out) << "var result = " << result_type << "()" << endl;
+
+ indent(out) << "do";
+ block_open(out);
+
+ indent(out);
+ if (!tfunction->get_returntype()->is_void()) {
+ out << "result.success = ";
+ }
+ out << "try handler." << (gen_cocoa_ ? function_name(tfunction) : tfunction->get_name()) << "(";
+
+ t_struct* arg_struct = tfunction->get_arglist();
+ const vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ string fieldName = (*f_iter)->get_name();
+ if (!gen_cocoa_ || f_iter != fields.begin()) {
+ out << fieldName << ": ";
+ }
+
+ out << "args." << fieldName;
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+
+ out << ")" << endl;
+ block_close(out);
+
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xfields = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+
+ if (!gen_cocoa_) {
+ for (x_iter = xfields.begin(); x_iter != xfields.end(); ++x_iter) {
+ indent(out) << "catch let error as ";
+
+ t_program* program = (*x_iter)->get_type()->get_program();
+ if ((*x_iter)->get_type()->get_name() == "Error" && namespaced_ && program != program_) {
+ out << get_real_swift_module(program) << ".";
+ }
+ out << (*x_iter)->get_type()->get_name();
+
+ out << " { result." << (*x_iter)->get_name() << " = error }" << endl;
+ }
+
+ indent(out) << "catch let error { throw error }" << endl;
+ out << endl;
+
+ if (!tfunction->is_oneway()) {
+ out << indent() << "try outProtocol.writeMessageBegin(name: \"" << tfunction->get_name() << "\", type: .reply, sequenceID: sequenceID)" << endl
+ << indent() << "try result.write(to: outProtocol)" << endl
+ << indent() << "try outProtocol.writeMessageEnd()" << endl;
+ }
+ } else {
+ for (x_iter = xfields.begin(); x_iter != xfields.end(); ++x_iter) {
+ indent(out) << "catch let error as " << (*x_iter)->get_type()->get_name();
+ block_open(out);
+ indent(out) << "result." << (*x_iter)->get_name() << " = error" << endl;
+ block_close(out);
+ }
+
+ indent(out) << "catch let error";
+ block_open(out);
+ out << indent() << "throw error" << endl;
+ block_close(out);
+
+ out << endl;
+
+ if (!tfunction->is_oneway()) {
+ out << indent() << "try outProtocol.writeMessageBeginWithName(\"" << tfunction->get_name() << "\", type: .REPLY, sequenceID: sequenceID)" << endl
+ << indent() << "try " << result_type << ".writeValue(result, toProtocol: outProtocol)" << endl
+ << indent() << "try outProtocol.writeMessageEnd()" << endl;
+ }
+ }
+ }
+ block_close(out);
+
+ }
+
+ indent(out) << "return processorHandlers" << endl;
+
+ block_close(out,false);
+ out << "()" << endl;
+ out << endl;
+
+ if (!gen_cocoa_) {
+ indent(out) << "public func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws";
+ } else {
+ indent(out) << "public func processOnInputProtocol(inProtocol: TProtocol, outputProtocol outProtocol: TProtocol) throws";
+ }
+ block_open(out);
+
+ out << endl;
+ out << indent() << "let (messageName, _, sequenceID) = try inProtocol.readMessageBegin()" << endl
+ << endl
+ << indent() << "if let processorHandler = " << name << ".processorHandlers[messageName]";
+ block_open(out);
+ out << indent() << "do";
+ block_open(out);
+ out << indent() << "try processorHandler(sequenceID, inProtocol, outProtocol, service)" << endl;
+ block_close(out);
+ if (!gen_cocoa_) {
+ out << indent() << "catch let error as TApplicationError";
+ block_open(out);
+ out << indent() << "try outProtocol.writeException(messageName: messageName, sequenceID: sequenceID, ex: error)" << endl;
+ block_close(out);
+ block_close(out);
+ out << indent() << "else";
+ block_open(out);
+ out << indent() << "try inProtocol.skip(type: .struct)" << endl
+ << indent() << "try inProtocol.readMessageEnd()" << endl
+ << indent() << "let ex = TApplicationError(error: .unknownMethod(methodName: messageName))" << endl
+ << indent() << "try outProtocol.writeException(messageName: messageName, "
+ << "sequenceID: sequenceID, ex: ex)" << endl;
+ } else {
+ out << indent() << "catch let error as NSError";
+ block_open(out);
+ out << indent() << "try outProtocol.writeExceptionForMessageName(messageName, sequenceID: sequenceID, ex: error)" << endl;
+ block_close(out);
+ block_close(out);
+ out << indent() << "else";
+ block_open(out);
+ out << indent() << "try inProtocol.skipType(.STRUCT)" << endl
+ << indent() << "try inProtocol.readMessageEnd()" << endl
+ << indent() << "try outProtocol.writeExceptionForMessageName(messageName," << endl;
+ indent_up();
+ out << indent() << "sequenceID: sequenceID," << endl
+ << indent() << "ex: NSError(" << endl;
+ indent_up();
+ out << indent() << "domain: TApplicationErrorDomain, " << endl
+ << indent() << "code: Int(TApplicationError.UnknownMethod.rawValue), " << endl
+ << indent() << "userInfo: [TApplicationErrorMethodKey: messageName]))" << endl;
+ indent_down();
+ indent_down();
+ }
+
+ block_close(out);
+ block_close(out);
+ block_close(out);
+ out << endl;
+}
+
+/**
+ * Returns an Swift name
+ *
+ * @param ttype The type
+ * @param class_ref Do we want a Class reference istead of a type reference?
+ * @return Swift type name, i.e. Dictionary<Key,Value>
+ */
+string t_swift_generator::type_name(t_type* ttype, bool is_optional, bool is_forced) {
+ string result = "";
+
+ if (ttype->is_base_type()) {
+ result += base_type_name((t_base_type*)ttype);
+ } else if (ttype->is_map()) {
+ t_map *map = (t_map *)ttype;
+ result += "TMap<" + type_name(map->get_key_type()) + ", " + type_name(map->get_val_type()) + ">";
+ } else if (ttype->is_set()) {
+ t_set *set = (t_set *)ttype;
+ result += "TSet<" + type_name(set->get_elem_type()) + ">";
+ } else if (ttype->is_list()) {
+ t_list *list = (t_list *)ttype;
+ result += "TList<" + type_name(list->get_elem_type()) + ">";
+ }
+ else {
+ t_program* program = ttype->get_program();
+ if (namespaced_ && program != program_) {
+ result += get_real_swift_module(program) + ".";
+ }
+ result += ttype->get_name();
+ }
+
+ if (is_optional) {
+ result += "?";
+ }
+ if (is_forced) {
+ result += "!";
+ }
+
+ return result;
+}
+
+/**
+ * Returns the Swift type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ */
+string t_swift_generator::base_type_name(t_base_type* type) {
+ t_base_type::t_base tbase = type->get_base();
+
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "Void";
+ case t_base_type::TYPE_STRING:
+ if (type->is_binary()) {
+ return gen_cocoa_ ? "TBinary" : "Data";
+ } else {
+ return "String";
+ }
+ case t_base_type::TYPE_BOOL:
+ return "Bool";
+ case t_base_type::TYPE_I8:
+ return "Int8";
+ case t_base_type::TYPE_I16:
+ return "Int16";
+ case t_base_type::TYPE_I32:
+ return "Int32";
+ case t_base_type::TYPE_I64:
+ return "Int64";
+ case t_base_type::TYPE_DOUBLE:
+ return "Double";
+ default:
+ throw "compiler error: no Swift name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+/**
+ * Renders full constant value (as would be seen after an '=')
+ *
+ */
+void t_swift_generator::render_const_value(ostream& out,
+ t_type* type,
+ t_const_value* value) {
+ type = get_true_type(type);
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ out << "\"" << get_escaped_string(value) << "\"";
+ break;
+ case t_base_type::TYPE_BOOL:
+ out << ((value->get_integer() > 0) ? "true" : "false");
+ break;
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ out << type_name(type) << "(" << value->get_integer() << ")";
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ out << type_name(type) << "(";
+ if (value->get_type() == t_const_value::CV_INTEGER) {
+ out << value->get_integer();
+ } else {
+ out << value->get_double();
+ }
+ out << ")";
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
+ }
+ } else if (type->is_enum()) {
+ out << (gen_cocoa_ ? value->get_identifier() : enum_const_name(value->get_identifier())); // Swift2/Cocoa compatibility
+ } else if (type->is_struct() || type->is_xception()) {
+
+ out << type_name(type) << "(";
+
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ t_field* tfield = *f_iter;
+ t_const_value* value = NULL;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ if (tfield->get_name() == v_iter->first->get_string()) {
+ value = v_iter->second;
+ }
+ }
+
+ if (value) {
+ out << tfield->get_name() << ": ";
+ render_const_value(out, tfield->get_type(), value);
+ }
+ else if (!field_is_optional(tfield)) {
+ throw "constant error: required field " + type->get_name() + "." + tfield->get_name() + " has no value";
+ }
+
+ if (++f_iter != fields.end()) {
+ out << ", ";
+ }
+ }
+
+ out << ")";
+
+ } else if (type->is_map()) {
+
+ out << "[";
+
+ t_type* ktype = ((t_map*)type)->get_key_type();
+ t_type* vtype = ((t_map*)type)->get_val_type();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end();) {
+
+ render_const_value(out, ktype, v_iter->first);
+ out << ": ";
+ render_const_value(out, vtype, v_iter->second);
+
+ if (++v_iter != val.end()) {
+ out << ", ";
+ }
+ }
+
+ out << "]";
+
+ } else if (type->is_list()) {
+
+ out << "[";
+
+ t_type* etype = ((t_list*)type)->get_elem_type();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end();) {
+
+ render_const_value(out, etype, v_iter->first);
+
+ if (++v_iter != val.end()) {
+ out << ", ";
+ }
+ }
+
+ out << "]";
+
+ } else if (type->is_set()) {
+
+ out << "[";
+
+ t_type* etype = ((t_set*)type)->get_elem_type();
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ for (v_iter = val.begin(); v_iter != val.end();) {
+
+ render_const_value(out, etype, v_iter->first);
+
+ if (++v_iter != val.end()) {
+ out << ", ";
+ }
+ }
+
+ out << "]";
+
+ } else {
+ throw "compiler error: no const of type " + type->get_name();
+ }
+
+}
+
+/**
+ * Declares an Swift property.
+ *
+ * @param tfield The field to declare a property for
+ */
+string t_swift_generator::declare_property(t_field* tfield, bool is_private) {
+
+ string visibility = is_private ? (gen_cocoa_ ? "private" : "fileprivate") : "public";
+
+ ostringstream render;
+
+ render << visibility << " var " << maybe_escape_identifier(tfield->get_name());
+
+ if (field_is_optional(tfield)) {
+ render << (gen_cocoa_ ? " " : "") << ": " << type_name(tfield->get_type(), true);
+ }
+ else {
+ if (!gen_cocoa_) {
+ render << ": " << type_name(tfield->get_type(), false);
+ } else {
+ // Swift2/Cocoa backward compat, Bad, default init
+ render << " = " << type_name(tfield->get_type(), false) << "()";
+ }
+ }
+
+ return render.str();
+}
+
+/**
+ * Renders a function signature
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_swift_generator::function_signature(t_function* tfunction) {
+
+ string result = "func " + (gen_cocoa_ ? function_name(tfunction) : tfunction->get_name());
+
+ result += "(" + argument_list(tfunction->get_arglist(), "", false) + ") throws"; /// argsreview
+
+ t_type* ttype = tfunction->get_returntype();
+ if (!ttype->is_void()) {
+ result += " -> " + type_name(ttype);
+ }
+
+ return result;
+}
+
+/**
+ * Renders a function docstring
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+void t_swift_generator::function_docstring(ostream& out, t_function* tfunction) {
+
+ // Generate docstring with following format:
+ // /// <Description>
+ // /// <empty line>
+ // /// - Parameters:
+ // /// - <parameter>: <parameter docstring>
+ // /// - Returns: <return type> (Thrift has no docstring on return val)
+ // /// - Throws: <exception types>
+
+ // Description
+ string doc = tfunction->get_doc();
+ generate_docstring(out, doc);
+ indent(out) << "///" << endl;
+
+ // Parameters
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ if (!fields.empty()) {
+ indent(out) << "/// - Parameters:" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "/// - " << (*f_iter)->get_name() << ": ";
+ string doc = (*f_iter)->get_doc();
+ if (!doc.empty() && doc[doc.length()-1] == '\n') {
+ doc.erase(doc.length()-1);
+ }
+ out << doc << endl;
+ }
+ }
+
+ // Returns
+ t_type* ttype = tfunction->get_returntype();
+ if (!ttype->is_void()) {
+ indent(out) << "/// - Returns: " << type_name(ttype) << endl;
+ }
+
+ // Throws
+ indent(out) << "/// - Throws: ";
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << type_name((*x_iter)->get_type());
+ if (*x_iter != xceptions.back()) {
+ out << ", ";
+ } }
+ out << endl;
+}
+
+/**
+ * Renders a function docstring
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+void t_swift_generator::async_function_docstring(ostream& out, t_function* tfunction) {
+ // Generate docstring with following format:
+ // /// <Description>
+ // /// <empty line>
+ // /// - Parameters:
+ // /// - <parameter>: <parameter docstring>
+ // /// - callback: <callback types>
+
+ // Description
+ string doc = tfunction->get_doc();
+ generate_docstring(out, doc);
+ indent(out) << "///" << endl;
+
+ // Parameters
+ const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
+ vector<t_field*>::const_iterator f_iter;
+ if (!fields.empty()) {
+ indent(out) << "/// - Parameters:" << endl;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ indent(out) << "/// - " << (*f_iter)->get_name() << ": ";
+ string doc = (*f_iter)->get_doc();
+ if (!doc.empty() && doc[doc.length()-1] == '\n') {
+ doc.erase(doc.length()-1);
+ }
+ out << doc << endl;
+ }
+ }
+
+ // completion
+ indent(out) << "/// - completion: TAsyncResult<" << type_name(tfunction->get_returntype())
+ << "> wrapping return and following Exceptions: ";
+ t_struct* xs = tfunction->get_xceptions();
+ const vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ out << type_name((*x_iter)->get_type());
+ if (*x_iter != xceptions.back()) {
+ out << ", ";
+ }
+ }
+ out << endl;
+}
+
+/**
+ * Renders a function signature that returns asynchronously via blocks.
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_swift_generator::async_function_signature(t_function* tfunction) {
+ t_type* ttype = tfunction->get_returntype();
+ t_struct* targlist = tfunction->get_arglist();
+ string result = "func " + (gen_cocoa_ ? function_name(tfunction) : tfunction->get_name());
+
+ if (!gen_cocoa_) {
+ string response_string = "(TAsyncResult<";
+ response_string += (ttype->is_void()) ? "Void" : type_name(ttype);
+ response_string += ">) -> Void";
+ result += "(" + argument_list(tfunction->get_arglist(), "", false)
+ + (targlist->get_members().size() ? ", " : "")
+ + "completion: @escaping " + response_string + ")";
+ } else {
+ string response_param = "(" + ((ttype->is_void()) ? "" : type_name(ttype)) + ") -> Void";
+ result += "(" + argument_list(tfunction->get_arglist(), "", false)
+ + (targlist->get_members().size() ? ", " : "")
+ + "success: " + response_param + ", "
+ + "failure: (NSError) -> Void) throws";
+ }
+ return result;
+}
+
+/**
+ * Renders a function signature that returns asynchronously via promises.
+ * ONLY FOR Swift2/Cocoa BACKWARDS COMPATIBILITY
+ *
+ * @param tfunction Function definition
+ * @return String of rendered function definition
+ */
+string t_swift_generator::promise_function_signature(t_function* tfunction) {
+ return "func " + function_name(tfunction) + "(" + argument_list(tfunction->get_arglist(), "", false) + ") throws "
+ + "-> Promise<" + type_name(tfunction->get_returntype()) + ">";
+}
+
+/**
+ * Renders a verbose function name suitable for a Swift method. ONLY FOR Swift2/Cocoa BACKWARDS COMPATIBILITY
+ */
+string t_swift_generator::function_name(t_function* tfunction) {
+ string name = tfunction->get_name();
+ if (!tfunction->get_arglist()->get_members().empty()) {
+ string first_arg = tfunction->get_arglist()->get_members().front()->get_name();
+ if (name.size() < first_arg.size() ||
+ lowercase(name.substr(name.size()-first_arg.size())) != lowercase(first_arg)) {
+ name += "With" + capitalize(tfunction->get_arglist()->get_members()[0]->get_name());
+ }
+ }
+ return name;
+}
+
+/**
+ * Renders a Swift method argument list
+ */
+string t_swift_generator::argument_list(t_struct* tstruct, string protocol_name, bool is_internal) {
+ string result = "";
+ bool include_protocol = !protocol_name.empty();
+
+ const vector<t_field*>& fields = tstruct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ if (include_protocol) {
+ result += protocol_name + ": TProtocol";
+ if (!fields.empty()) {
+ result += ", ";
+ }
+ } else if (!fields.empty() && is_internal && gen_cocoa_) {
+ // Force first argument to be named, Swift2/Cocoa backwards compat
+ result += fields.front()->get_name() + " ";
+ }
+
+ for (f_iter = fields.begin(); f_iter != fields.end();) {
+ t_field* arg = *f_iter;
+
+ if (!gen_cocoa_) {
+ // optional args not usually permitted for some reason, even though dynamic langs handle it
+ // use annotation "swift.nullable" to achieve
+ result += arg->get_name() + ": " + type_name(arg->get_type(), field_is_optional(arg));
+ } else {
+ result += arg->get_name() + ": " + type_name(arg->get_type());
+ }
+
+ if (++f_iter != fields.end()) {
+ result += ", ";
+ }
+ }
+ return result;
+}
+
+/**
+ * https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html
+ *
+ */
+
+void t_swift_generator::populate_reserved_words() {
+ if (!gen_cocoa_) {
+ swift_reserved_words_.insert("__COLUMN__");
+ swift_reserved_words_.insert("__FILE__");
+ swift_reserved_words_.insert("__FUNCTION__");
+ swift_reserved_words_.insert("__LINE__");
+ swift_reserved_words_.insert("Any");
+ swift_reserved_words_.insert("as");
+ swift_reserved_words_.insert("associatedtype");
+ swift_reserved_words_.insert("associativity");
+ swift_reserved_words_.insert("break");
+ swift_reserved_words_.insert("case");
+ swift_reserved_words_.insert("catch");
+ swift_reserved_words_.insert("class");
+ swift_reserved_words_.insert("continue");
+ swift_reserved_words_.insert("convenience");
+ swift_reserved_words_.insert("default");
+ swift_reserved_words_.insert("defer");
+ swift_reserved_words_.insert("deinit");
+ swift_reserved_words_.insert("didSet");
+ swift_reserved_words_.insert("do");
+ swift_reserved_words_.insert("dynamic");
+ swift_reserved_words_.insert("dynamicType");
+ swift_reserved_words_.insert("else");
+ swift_reserved_words_.insert("enum");
+ swift_reserved_words_.insert("extension");
+ swift_reserved_words_.insert("fallthrough");
+ swift_reserved_words_.insert("false");
+ swift_reserved_words_.insert("fileprivate");
+ swift_reserved_words_.insert("final");
+ swift_reserved_words_.insert("for");
+ swift_reserved_words_.insert("func");
+ swift_reserved_words_.insert("get");
+ swift_reserved_words_.insert("guard");
+ swift_reserved_words_.insert("if");
+ swift_reserved_words_.insert("import");
+ swift_reserved_words_.insert("in");
+ swift_reserved_words_.insert("indirect");
+ swift_reserved_words_.insert("infix");
+ swift_reserved_words_.insert("init");
+ swift_reserved_words_.insert("inout");
+ swift_reserved_words_.insert("internal");
+ swift_reserved_words_.insert("is");
+ swift_reserved_words_.insert("lazy");
+ swift_reserved_words_.insert("left");
+ swift_reserved_words_.insert("let");
+ swift_reserved_words_.insert("mutating");
+ swift_reserved_words_.insert("nil");
+ swift_reserved_words_.insert("none");
+ swift_reserved_words_.insert("nonmutating");
+ swift_reserved_words_.insert("open");
+ swift_reserved_words_.insert("operator");
+ swift_reserved_words_.insert("optional");
+ swift_reserved_words_.insert("override");
+ swift_reserved_words_.insert("postfix");
+ swift_reserved_words_.insert("precedence");
+ swift_reserved_words_.insert("prefix");
+ swift_reserved_words_.insert("private");
+ swift_reserved_words_.insert("protocol");
+ swift_reserved_words_.insert("Protocol");
+ swift_reserved_words_.insert("public");
+ swift_reserved_words_.insert("repeat");
+ swift_reserved_words_.insert("required");
+ swift_reserved_words_.insert("rethrows");
+ swift_reserved_words_.insert("return");
+ swift_reserved_words_.insert("right");
+ swift_reserved_words_.insert("self");
+ swift_reserved_words_.insert("Self");
+ swift_reserved_words_.insert("set");
+ swift_reserved_words_.insert("static");
+ swift_reserved_words_.insert("struct");
+ swift_reserved_words_.insert("subscript");
+ swift_reserved_words_.insert("super");
+ swift_reserved_words_.insert("switch");
+ swift_reserved_words_.insert("throw");
+ swift_reserved_words_.insert("throws");
+ swift_reserved_words_.insert("true");
+ swift_reserved_words_.insert("try");
+ swift_reserved_words_.insert("Type");
+ swift_reserved_words_.insert("typealias");
+ swift_reserved_words_.insert("unowned");
+ swift_reserved_words_.insert("var");
+ swift_reserved_words_.insert("weak");
+ swift_reserved_words_.insert("where");
+ swift_reserved_words_.insert("while");
+ swift_reserved_words_.insert("willSet");
+ } else {
+ swift_reserved_words_.insert("Self");
+ swift_reserved_words_.insert("associatedtype");
+ swift_reserved_words_.insert("defer");
+ swift_reserved_words_.insert("deinit");
+ swift_reserved_words_.insert("dynamicType");
+ swift_reserved_words_.insert("enum");
+ swift_reserved_words_.insert("extension");
+ swift_reserved_words_.insert("fallthrough");
+ swift_reserved_words_.insert("false");
+ swift_reserved_words_.insert("func");
+ swift_reserved_words_.insert("guard");
+ swift_reserved_words_.insert("init");
+ swift_reserved_words_.insert("inout");
+ swift_reserved_words_.insert("internal");
+ swift_reserved_words_.insert("let");
+ swift_reserved_words_.insert("operator");
+ swift_reserved_words_.insert("protocol");
+ swift_reserved_words_.insert("repeat");
+ swift_reserved_words_.insert("rethrows");
+ swift_reserved_words_.insert("struct");
+ swift_reserved_words_.insert("subscript");
+ swift_reserved_words_.insert("throws");
+ swift_reserved_words_.insert("true");
+ swift_reserved_words_.insert("typealias");
+ swift_reserved_words_.insert("where");
+ }
+}
+
+string t_swift_generator::maybe_escape_identifier(const string& identifier) {
+ if (swift_reserved_words_.find(identifier) != swift_reserved_words_.end()) {
+ return "`" + identifier + "`";
+ }
+ return identifier;
+}
+
+/**
+ * Converts the parse type to a Swift TType enumeration.
+ */
+string t_swift_generator::type_to_enum(t_type* type, bool qualified) {
+ type = get_true_type(type);
+
+ string result = qualified ? "TType." : ".";
+ if (!gen_cocoa_) {
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return result + "string";
+ case t_base_type::TYPE_BOOL:
+ return result + "bool";
+ case t_base_type::TYPE_I8:
+ return result + "i8";
+ case t_base_type::TYPE_I16:
+ return result + "i16";
+ case t_base_type::TYPE_I32:
+ return result + "i32";
+ case t_base_type::TYPE_I64:
+ return result + "i64";
+ case t_base_type::TYPE_DOUBLE:
+ return result + "double";
+ }
+ } else if (type->is_enum()) {
+ return result + "i32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return result + "struct";
+ } else if (type->is_map()) {
+ return result + "map";
+ } else if (type->is_set()) {
+ return result + "set";
+ } else if (type->is_list()) {
+ return result + "list";
+ }
+ } else {
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ throw "NO T_VOID CONSTRUCT";
+ case t_base_type::TYPE_STRING:
+ return result + "STRING";
+ case t_base_type::TYPE_BOOL:
+ return result + "BOOL";
+ case t_base_type::TYPE_I8:
+ return result + "BYTE";
+ case t_base_type::TYPE_I16:
+ return result + "I16";
+ case t_base_type::TYPE_I32:
+ return result + "I32";
+ case t_base_type::TYPE_I64:
+ return result + "I64";
+ case t_base_type::TYPE_DOUBLE:
+ return result + "DOUBLE";
+ }
+ } else if (type->is_enum()) {
+ return result + "I32";
+ } else if (type->is_struct() || type->is_xception()) {
+ return result + "STRUCT";
+ } else if (type->is_map()) {
+ return result + "MAP";
+ } else if (type->is_set()) {
+ return result + "SET";
+ } else if (type->is_list()) {
+ return result + "LIST";
+ }
+ }
+
+ throw "INVALID TYPE IN type_to_enum: " + type->get_name();
+}
+
+
+THRIFT_REGISTER_GENERATOR(
+ swift,
+ "Swift 3.0",
+ " log_unexpected: Log every time an unexpected field ID or type is encountered.\n"
+ " debug_descriptions:\n"
+ " Allow use of debugDescription so the app can add description via a cateogory/extension\n"
+ " async_clients: Generate clients which invoke asynchronously via block syntax.\n"
+ " namespaced: Generate source in Module scoped output directories for Swift Namespacing.\n"
+ " cocoa: Generate Swift 2.x code compatible with the Thrift/Cocoa library\n"
+ " promise_kit: Generate clients which invoke asynchronously via promises (only use with cocoa flag)\n"
+ " safe_enums: Generate enum types with an unknown case to handle unspecified values rather than throw a serialization error\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc
new file mode 100644
index 000000000..b6692938f
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc
@@ -0,0 +1,692 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <limits>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+
+#include "thrift/platform.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+using std::stack;
+using std::set;
+
+static const string endl = "\n";
+static const string quot = "\"";
+
+static const string default_ns_prefix = "http://thrift.apache.org/xml/ns/";
+
+/**
+ * This generator creates an XML model of the parsed IDL tree, and is designed
+ * to make it easy to use this file as the input for other template engines,
+ * such as XSLT. To this end, the generated XML is slightly more verbose than
+ * you might expect... for example, references to "id" types (such as structs,
+ * unions, etc) always specify the name of the IDL document, even if the type
+ * is defined in the same document as the reference.
+ */
+class t_xml_generator : public t_generator {
+public:
+ t_xml_generator( t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ should_merge_includes_ = false;
+ should_use_default_ns_ = true;
+ should_use_namespaces_ = true;
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ if( iter->first.compare("merge") == 0) {
+ should_merge_includes_ = true;
+ } else if( iter->first.compare("no_default_ns") == 0) {
+ should_use_default_ns_ = false;
+ } else if( iter->first.compare("no_namespaces") == 0) {
+ should_use_namespaces_ = false;
+ } else {
+ throw "unknown option xml:" + iter->first;
+ }
+ }
+
+ out_dir_base_ = "gen-xml";
+ }
+
+ ~t_xml_generator() override = default;
+
+ void init_generator() override;
+ void close_generator() override;
+ void generate_program() override;
+
+ void iterate_program(t_program* program);
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override;
+ void generate_function(t_function* tfunc);
+ void generate_field(t_field* field);
+
+ void generate_service(t_service* tservice) override;
+ void generate_struct(t_struct* tstruct) override;
+
+ void generate_annotations(std::map<std::string, std::string> annotations);
+
+private:
+ bool should_merge_includes_;
+ bool should_use_default_ns_;
+ bool should_use_namespaces_;
+
+ ofstream_with_content_based_conditional_update f_xml_;
+
+ std::set<string> programs_;
+ std::stack<string> elements_;
+ bool top_element_is_empty;
+ bool top_element_is_open;
+
+ string target_namespace(t_program* program);
+ void write_element_start(const string name);
+ void close_top_element();
+ void write_element_end();
+ void write_attribute(string key, string val);
+ void write_int_attribute(string key, int val);
+ string escape_xml_string(const string& input);
+
+ void write_xml_comment(string msg);
+
+ void write_type(t_type* ttype);
+ void write_doc(t_doc* tdoc);
+
+ template <typename T>
+ string number_to_string(T t) {
+ std::ostringstream out;
+ out.imbue(std::locale::classic());
+ out.precision(std::numeric_limits<T>::digits10);
+ out << t;
+ return out.str();
+ }
+
+ template <typename T>
+ void write_number(T n) {
+ f_xml_ << number_to_string(n);
+ }
+
+ template <typename T>
+ void write_element_number(string name, T n) {
+ write_element_string(name, number_to_string(n));
+ }
+
+ string get_type_name(t_type* ttype);
+
+ void generate_constant(t_const* con);
+
+ void write_element_string(string name, string value);
+ void write_value(t_type* tvalue);
+ void write_const_value(t_const_value* value);
+ virtual std::string xml_autogen_comment() {
+ return std::string("\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n";
+ }
+};
+
+void t_xml_generator::init_generator() {
+ MKDIR(get_out_dir().c_str());
+
+ string f_xml_name = get_out_dir() + program_->get_name() + ".xml";
+ f_xml_.open(f_xml_name.c_str());
+
+ top_element_is_open = false;
+}
+
+string t_xml_generator::target_namespace(t_program* program) {
+ std::map<std::string, std::string> map;
+ std::map<std::string, std::string>::iterator iter;
+ map = program->get_namespace_annotations("xml");
+ if ((iter = map.find("targetNamespace")) != map.end()) {
+ return iter->second;
+ }
+ map = program->get_namespaces();
+ if ((iter = map.find("xml")) != map.end()) {
+ return default_ns_prefix + iter->second;
+ }
+ map = program->get_namespace_annotations("*");
+ if ((iter = map.find("xml.targetNamespace")) != map.end()) {
+ return iter->second;
+ }
+ map = program->get_namespaces();
+ if ((iter = map.find("*")) != map.end()) {
+ return default_ns_prefix + iter->second;
+ }
+ return default_ns_prefix + program->get_name();
+}
+
+void t_xml_generator::write_xml_comment(string msg) {
+ close_top_element();
+ // TODO: indent any EOLs that may occur with msg
+ // TODO: proper msg escaping needed?
+ f_xml_ << indent() << "<!-- " << msg << " -->" << endl;
+ top_element_is_empty = false;
+}
+
+void t_xml_generator::close_top_element() {
+ if( top_element_is_open) {
+ top_element_is_open = false;
+ if (elements_.size() > 0 && top_element_is_empty) {
+ f_xml_ << ">" << endl;
+ }
+ }
+}
+
+void t_xml_generator::write_element_start(string name) {
+ if (should_use_namespaces_ && !should_use_default_ns_) {
+ name = "idl:" + name;
+ }
+ close_top_element();
+ f_xml_ << indent() << "<" << name;
+ elements_.push(name);
+ top_element_is_empty = true;
+ top_element_is_open = true;
+ indent_up();
+}
+
+void t_xml_generator::write_element_end() {
+ indent_down();
+ if (top_element_is_empty && top_element_is_open) {
+ f_xml_ << " />" << endl;
+ } else {
+ f_xml_ << indent() << "</" << elements_.top() << ">" << endl;
+ }
+ top_element_is_empty = false;
+ elements_.pop();
+}
+
+void t_xml_generator::write_attribute(string key, string val) {
+ f_xml_ << " " << key << "=\"" << escape_xml_string(val) << "\"";
+}
+
+void t_xml_generator::write_int_attribute(string key, int val) {
+ write_attribute(key, number_to_string(val));
+}
+
+void t_xml_generator::write_element_string(string name, string val) {
+ if (should_use_namespaces_ && !should_use_default_ns_) {
+ name = "idl:" + name;
+ }
+ close_top_element();
+ top_element_is_empty = false;
+ f_xml_ << indent()
+ << "<" << name << ">" << escape_xml_string(val) << "</" << name << ">"
+ << endl;
+}
+
+string t_xml_generator::escape_xml_string(const string& input) {
+ std::ostringstream ss;
+ for (char iter : input) {
+ switch (iter) {
+ case '&':
+ ss << "&amp;";
+ break;
+ case '"':
+ ss << "&quot;";
+ break;
+ case '\'':
+ ss << "&apos;";
+ break;
+ case '<':
+ ss << "&lt;";
+ break;
+ case '>':
+ ss << "&gt;";
+ break;
+ default:
+ ss << iter;
+ break;
+ }
+ }
+ return ss.str();
+}
+
+void t_xml_generator::close_generator() {
+ f_xml_.close();
+}
+
+void t_xml_generator::generate_program() {
+
+ init_generator();
+
+ write_element_start("idl");
+ if (should_use_namespaces_) {
+ if (should_use_default_ns_) {
+ write_attribute("xmlns", "http://thrift.apache.org/xml/idl");
+ }
+ write_attribute("xmlns:idl", "http://thrift.apache.org/xml/idl");
+ }
+
+ write_xml_comment( xml_autogen_comment());
+
+ iterate_program(program_);
+
+ write_element_end();
+
+ close_generator();
+
+}
+
+void t_xml_generator::iterate_program(t_program* program) {
+
+ write_element_start("document");
+ write_attribute("name", program->get_name());
+ if (should_use_namespaces_) {
+ const string targetNamespace = target_namespace(program);
+ write_attribute("targetNamespace", targetNamespace);
+ write_attribute("xmlns:" + program->get_name(), targetNamespace);
+ }
+ write_doc(program);
+
+ const vector<t_program*> includes = program->get_includes();
+ vector<t_program*>::const_iterator inc_it;
+ for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) {
+ write_element_start("include");
+ write_attribute("name", (*inc_it)->get_name());
+ write_element_end();
+ }
+
+ const map<string, string>& namespaces = program->get_namespaces();
+ map<string, string>::const_iterator ns_it;
+ for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) {
+ write_element_start("namespace");
+ write_attribute("name", ns_it->first);
+ write_attribute("value", ns_it->second);
+ generate_annotations(program->get_namespace_annotations(ns_it->first));
+ write_element_end();
+ }
+
+ // TODO: can constants have annotations?
+ vector<t_const*> consts = program->get_consts();
+ vector<t_const*>::iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ generate_constant(*c_iter);
+ }
+
+ vector<t_typedef*> typedefs = program->get_typedefs();
+ vector<t_typedef*>::iterator td_iter;
+ for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+ generate_typedef(*td_iter);
+ }
+
+ vector<t_enum*> enums = program->get_enums();
+ vector<t_enum*>::iterator en_iter;
+ for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+ generate_enum(*en_iter);
+ }
+
+ vector<t_struct*> objects = program->get_objects();
+ vector<t_struct*>::iterator o_iter;
+ for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+ if ((*o_iter)->is_xception()) {
+ generate_xception(*o_iter);
+ } else {
+ generate_struct(*o_iter);
+ }
+ }
+
+ vector<t_service*> services = program->get_services();
+ vector<t_service*>::iterator sv_iter;
+ for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+ generate_service(*sv_iter);
+ }
+
+ write_element_end();
+
+ if (should_merge_includes_) {
+ programs_.insert(program->get_name());
+ const vector<t_program*> programs = program->get_includes();
+ vector<t_program*>::const_iterator prog_it;
+ for (prog_it = programs.begin(); prog_it != programs.end(); ++prog_it) {
+ if (!programs_.count((*prog_it)->get_name())) {
+ iterate_program(*prog_it);
+ }
+ }
+ }
+
+}
+
+void t_xml_generator::generate_typedef(t_typedef* ttypedef) {
+ write_element_start("typedef");
+ write_attribute("name", ttypedef->get_name());
+ write_doc(ttypedef);
+ write_type(ttypedef->get_true_type());
+ generate_annotations(ttypedef->annotations_);
+ write_element_end();
+ return;
+}
+
+void t_xml_generator::write_type(t_type* ttype) {
+ const string type = get_type_name(ttype);
+ write_attribute("type", type);
+ if (type == "id") {
+ write_attribute("type-module", ttype->get_program()->get_name());
+ write_attribute("type-id", ttype->get_name());
+ } else if (type == "list") {
+ t_type* etype = ((t_list*)ttype)->get_elem_type();
+ write_element_start("elemType");
+ write_type(etype);
+ write_element_end();
+ } else if (type == "set") {
+ t_type* etype = ((t_set*)ttype)->get_elem_type();
+ write_element_start("elemType");
+ write_type(etype);
+ write_element_end();
+ } else if (type == "map") {
+ t_type* ktype = ((t_map*)ttype)->get_key_type();
+ write_element_start("keyType");
+ write_type(ktype);
+ write_element_end();
+ t_type* vtype = ((t_map*)ttype)->get_val_type();
+ write_element_start("valueType");
+ write_type(vtype);
+ write_element_end();
+ }
+}
+
+void t_xml_generator::write_doc(t_doc* tdoc) {
+ if (tdoc->has_doc()) {
+ string doc = tdoc->get_doc();
+ // for some reason there always seems to be a trailing newline on doc
+ // comments; loop below naively tries to strip off trailing cr/lf
+ int n = 0;
+ for (string::reverse_iterator i = doc.rbegin(); i != doc.rend(); i++,n++) {
+ if (*i != '\n' || *i == '\r') {
+ if (n > 0) {
+ doc.erase(doc.length() - n);
+ }
+ break;
+ }
+ }
+ write_attribute("doc", doc);
+ }
+}
+
+void t_xml_generator::generate_annotations(
+ std::map<std::string, std::string> annotations) {
+ std::map<std::string, std::string>::iterator iter;
+ for (iter = annotations.begin(); iter != annotations.end(); ++iter) {
+ write_element_start("annotation");
+ write_attribute("key", iter->first);
+ write_attribute("value", iter->second);
+ write_element_end();
+ }
+}
+
+void t_xml_generator::generate_constant(t_const* con) {
+ write_element_start("const");
+ write_attribute("name", con->get_name());
+ write_doc(con);
+ write_type(con->get_type());
+ write_const_value(con->get_value());
+ write_element_end();
+}
+
+void t_xml_generator::write_const_value(t_const_value* value) {
+
+ switch (value->get_type()) {
+
+ case t_const_value::CV_IDENTIFIER:
+ case t_const_value::CV_INTEGER:
+ write_element_number("int", value->get_integer());
+ break;
+
+ case t_const_value::CV_DOUBLE:
+ write_element_number("double", value->get_double());
+ break;
+
+ case t_const_value::CV_STRING:
+ write_element_string("string", value->get_string());
+ break;
+
+ case t_const_value::CV_LIST: {
+ write_element_start("list");
+ std::vector<t_const_value*> list = value->get_list();
+ std::vector<t_const_value*>::iterator lit;
+ for (lit = list.begin(); lit != list.end(); ++lit) {
+ write_element_start("entry");
+ write_const_value(*lit);
+ write_element_end();
+ }
+ write_element_end();
+ break;
+ }
+
+ case t_const_value::CV_MAP: {
+ write_element_start("map");
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare> map = value->get_map();
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator mit;
+ for (mit = map.begin(); mit != map.end(); ++mit) {
+ write_element_start("entry");
+ write_element_start("key");
+ write_const_value(mit->first);
+ write_element_end();
+ write_element_start("value");
+ write_const_value(mit->second);
+ write_element_end();
+ write_element_end();
+ }
+ write_element_end();
+ break;
+ }
+
+ default:
+ indent_up();
+ f_xml_ << indent() << "<null />" << endl;
+ indent_down();
+ break;
+ }
+
+}
+
+void t_xml_generator::generate_enum(t_enum* tenum) {
+
+ write_element_start("enum");
+ write_attribute("name", tenum->get_name());
+ write_doc(tenum);
+
+ vector<t_enum_value*> values = tenum->get_constants();
+ vector<t_enum_value*>::iterator val_iter;
+ for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
+ t_enum_value* val = (*val_iter);
+ write_element_start("member");
+ write_attribute("name", val->get_name());
+ write_int_attribute("value", val->get_value());
+ write_doc(val);
+ generate_annotations(val->annotations_);
+ write_element_end();
+ }
+
+ generate_annotations(tenum->annotations_);
+
+ write_element_end();
+
+}
+
+void t_xml_generator::generate_struct(t_struct* tstruct) {
+
+ string tagname = "struct";
+ if (tstruct->is_union()) {
+ tagname = "union";
+ } else if (tstruct->is_xception()) {
+ tagname = "exception";
+ }
+
+ write_element_start(tagname);
+ write_attribute("name", tstruct->get_name());
+ write_doc(tstruct);
+ vector<t_field*> members = tstruct->get_members();
+ vector<t_field*>::iterator mem_iter;
+ for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) {
+ write_element_start("field");
+ generate_field(*mem_iter);
+ write_element_end();
+ }
+
+ generate_annotations(tstruct->annotations_);
+
+ write_element_end();
+
+}
+
+void t_xml_generator::generate_field(t_field* field) {
+ write_attribute("name", field->get_name());
+ write_int_attribute("field-id", field->get_key());
+ write_doc(field);
+ string requiredness;
+ switch (field->get_req()) {
+ case t_field::T_REQUIRED:
+ requiredness = "required";
+ break;
+ case t_field::T_OPTIONAL:
+ requiredness = "optional";
+ break;
+ default:
+ requiredness = "";
+ break;
+ }
+ if (requiredness != "") {
+ write_attribute("required", requiredness);
+ }
+ write_type(field->get_type());
+ if (field->get_value()) {
+ write_element_start("default");
+ write_const_value(field->get_value());
+ write_element_end();
+ }
+ generate_annotations(field->annotations_);
+}
+
+void t_xml_generator::generate_service(t_service* tservice) {
+
+ write_element_start("service");
+ write_attribute("name", tservice->get_name());
+
+ if (should_use_namespaces_) {
+ string prog_ns = target_namespace(tservice->get_program());
+ if (*prog_ns.rbegin() != '/') {
+ prog_ns.push_back('/');
+ }
+ const string tns = prog_ns + tservice->get_name();
+ write_attribute("targetNamespace", tns);
+ write_attribute("xmlns:tns", tns);
+ }
+
+ if (tservice->get_extends()) {
+ const t_service* extends = tservice->get_extends();
+ write_attribute("parent-module", extends->get_program()->get_name());
+ write_attribute("parent-id", extends->get_name());
+ }
+
+ write_doc(tservice);
+
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator fn_iter = functions.begin();
+ for (; fn_iter != functions.end(); fn_iter++) {
+ generate_function(*fn_iter);
+ }
+
+ generate_annotations(tservice->annotations_);
+
+ write_element_end();
+
+}
+
+void t_xml_generator::generate_function(t_function* tfunc) {
+
+ write_element_start("method");
+
+ write_attribute("name", tfunc->get_name());
+ if (tfunc->is_oneway()) {
+ write_attribute("oneway", "true");
+ }
+
+ write_doc(tfunc);
+
+ write_element_start("returns");
+ write_type(tfunc->get_returntype());
+ write_element_end();
+
+ vector<t_field*> members = tfunc->get_arglist()->get_members();
+ vector<t_field*>::iterator mem_iter = members.begin();
+ for (; mem_iter != members.end(); mem_iter++) {
+ write_element_start("arg");
+ generate_field(*mem_iter);
+ write_element_end();
+ }
+
+ vector<t_field*> excepts = tfunc->get_xceptions()->get_members();
+ vector<t_field*>::iterator ex_iter = excepts.begin();
+ for (; ex_iter != excepts.end(); ex_iter++) {
+ write_element_start("throws");
+ generate_field(*ex_iter);
+ write_element_end();
+ }
+
+ generate_annotations(tfunc->annotations_);
+
+ write_element_end();
+
+}
+
+string t_xml_generator::get_type_name(t_type* ttype) {
+ if (ttype->is_list()) {
+ return "list";
+ }
+ if (ttype->is_set()) {
+ return "set";
+ }
+ if (ttype->is_map()) {
+ return "map";
+ }
+ if ((ttype->is_enum() )||
+ (ttype->is_struct() )||
+ (ttype->is_typedef() )||
+ (ttype->is_xception())){
+ return "id";
+ }
+ if (ttype->is_base_type()) {
+ t_base_type* tbasetype = (t_base_type*)ttype;
+ if (tbasetype->is_binary() ) {
+ return "binary";
+ }
+ return t_base_type::t_base_name(tbasetype->get_base());
+ }
+ return "(unknown)";
+}
+
+THRIFT_REGISTER_GENERATOR(
+ xml,
+ "XML",
+ " merge: Generate output with included files merged\n"
+ " no_default_ns: Omit default xmlns and add idl: prefix to all elements\n"
+ " no_namespaces: Do not add namespace definitions to the XML model\n")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc
new file mode 100644
index 000000000..379e5a448
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "thrift/version.h"
+#include "thrift/platform.h"
+#include "thrift/generate/t_generator.h"
+
+using std::map;
+using std::ofstream;
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n"; // avoid ostream << std::endl flushes
+
+/**
+ * XSD generator, creates an XSD for the base types etc.
+ *
+ */
+class t_xsd_generator : public t_generator {
+public:
+ t_xsd_generator(t_program* program,
+ const std::map<std::string, std::string>& parsed_options,
+ const std::string& option_string)
+ : t_generator(program) {
+ (void)option_string;
+ std::map<std::string, std::string>::const_iterator iter;
+
+ /* no options yet */
+ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
+ throw "unknown option xsd:" + iter->first;
+ }
+
+ out_dir_base_ = "gen-xsd";
+ }
+
+ ~t_xsd_generator() override = default;
+
+ /**
+ * Init and close methods
+ */
+
+ void init_generator() override;
+ void close_generator() override;
+
+ /**
+ * Program-level generation functions
+ */
+
+ void generate_typedef(t_typedef* ttypedef) override;
+ void generate_enum(t_enum* tenum) override { (void)tenum; }
+
+ void generate_service(t_service* tservice) override;
+ void generate_struct(t_struct* tstruct) override;
+
+private:
+ void generate_element(std::ostream& out,
+ std::string name,
+ t_type* ttype,
+ t_struct* attrs = NULL,
+ bool optional = false,
+ bool nillable = false,
+ bool list_element = false);
+
+ std::string ns(std::string in, std::string ns) { return ns + ":" + in; }
+
+ std::string xsd(std::string in) { return ns(in, "xsd"); }
+
+ std::string type_name(t_type* ttype);
+ std::string base_type_name(t_base_type::t_base tbase);
+
+ virtual std::string xml_autogen_comment() {
+ return std::string("<!--\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
+ + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
+ + " -->\n";
+ }
+
+ /**
+ * Output xsd/php file
+ */
+ ofstream_with_content_based_conditional_update f_xsd_;
+ ofstream_with_content_based_conditional_update f_php_;
+
+ /**
+ * Output string stream
+ */
+ std::ostringstream s_xsd_types_;
+};
+
+void t_xsd_generator::init_generator() {
+ // Make output directory
+ MKDIR(get_out_dir().c_str());
+
+ // Make output file
+ string f_php_name = get_out_dir() + program_->get_name() + "_xsd.php";
+ f_php_.open(f_php_name.c_str());
+
+ f_php_ << "<?php" << endl
+ << autogen_comment() << endl;
+}
+
+void t_xsd_generator::close_generator() {
+ f_php_ << "?>" << endl;
+ f_php_.close();
+}
+
+void t_xsd_generator::generate_typedef(t_typedef* ttypedef) {
+ indent(s_xsd_types_) << "<xsd:simpleType name=\"" << ttypedef->get_name() << "\">" << endl;
+ indent_up();
+ if (ttypedef->get_type()->is_string() && ((t_base_type*)ttypedef->get_type())->is_string_enum()) {
+ indent(s_xsd_types_) << "<xsd:restriction base=\"" << type_name(ttypedef->get_type()) << "\">"
+ << endl;
+ indent_up();
+ const vector<string>& values = ((t_base_type*)ttypedef->get_type())->get_string_enum_vals();
+ vector<string>::const_iterator v_iter;
+ for (v_iter = values.begin(); v_iter != values.end(); ++v_iter) {
+ indent(s_xsd_types_) << "<xsd:enumeration value=\"" << (*v_iter) << "\" />" << endl;
+ }
+ indent_down();
+ indent(s_xsd_types_) << "</xsd:restriction>" << endl;
+ } else {
+ indent(s_xsd_types_) << "<xsd:restriction base=\"" << type_name(ttypedef->get_type()) << "\" />"
+ << endl;
+ }
+ indent_down();
+ indent(s_xsd_types_) << "</xsd:simpleType>" << endl << endl;
+}
+
+void t_xsd_generator::generate_struct(t_struct* tstruct) {
+ vector<t_field*>::const_iterator m_iter;
+ const vector<t_field*>& members = tstruct->get_members();
+ bool xsd_all = tstruct->get_xsd_all();
+
+ indent(s_xsd_types_) << "<xsd:complexType name=\"" << tstruct->get_name() << "\">" << endl;
+ indent_up();
+ if (xsd_all) {
+ indent(s_xsd_types_) << "<xsd:all>" << endl;
+ } else {
+ indent(s_xsd_types_) << "<xsd:sequence>" << endl;
+ }
+ indent_up();
+
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ generate_element(s_xsd_types_,
+ (*m_iter)->get_name(),
+ (*m_iter)->get_type(),
+ (*m_iter)->get_xsd_attrs(),
+ (*m_iter)->get_xsd_optional() || xsd_all,
+ (*m_iter)->get_xsd_nillable());
+ }
+
+ indent_down();
+ if (xsd_all) {
+ indent(s_xsd_types_) << "</xsd:all>" << endl;
+ } else {
+ indent(s_xsd_types_) << "</xsd:sequence>" << endl;
+ }
+ indent_down();
+ indent(s_xsd_types_) << "</xsd:complexType>" << endl << endl;
+}
+
+void t_xsd_generator::generate_element(ostream& out,
+ string name,
+ t_type* ttype,
+ t_struct* attrs,
+ bool optional,
+ bool nillable,
+ bool list_element) {
+ string sminOccurs = (optional || list_element) ? " minOccurs=\"0\"" : "";
+ string smaxOccurs = list_element ? " maxOccurs=\"unbounded\"" : "";
+ string soptional = sminOccurs + smaxOccurs;
+ string snillable = nillable ? " nillable=\"true\"" : "";
+
+ if (ttype->is_void() || ttype->is_list()) {
+ indent(out) << "<xsd:element name=\"" << name << "\"" << soptional << snillable << ">" << endl;
+ indent_up();
+ if (attrs == NULL && ttype->is_void()) {
+ indent(out) << "<xsd:complexType />" << endl;
+ } else {
+ indent(out) << "<xsd:complexType>" << endl;
+ indent_up();
+ if (ttype->is_list()) {
+ indent(out) << "<xsd:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" << endl;
+ indent_up();
+ string subname;
+ t_type* subtype = ((t_list*)ttype)->get_elem_type();
+ if (subtype->is_base_type() || subtype->is_container()) {
+ subname = name + "_elt";
+ } else {
+ subname = type_name(subtype);
+ }
+ f_php_ << "$GLOBALS['" << program_->get_name() << "_xsd_elt_" << name << "'] = '" << subname
+ << "';" << endl;
+ generate_element(out, subname, subtype, NULL, false, false, true);
+ indent_down();
+ indent(out) << "</xsd:sequence>" << endl;
+ indent(out) << "<xsd:attribute name=\"list\" type=\"xsd:boolean\" />" << endl;
+ }
+ if (attrs != NULL) {
+ const vector<t_field*>& members = attrs->get_members();
+ vector<t_field*>::const_iterator a_iter;
+ for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) {
+ indent(out) << "<xsd:attribute name=\"" << (*a_iter)->get_name() << "\" type=\""
+ << type_name((*a_iter)->get_type()) << "\" />" << endl;
+ }
+ }
+ indent_down();
+ indent(out) << "</xsd:complexType>" << endl;
+ }
+ indent_down();
+ indent(out) << "</xsd:element>" << endl;
+ } else {
+ if (attrs == NULL) {
+ indent(out) << "<xsd:element name=\"" << name << "\""
+ << " type=\"" << type_name(ttype) << "\"" << soptional << snillable << " />"
+ << endl;
+ } else {
+ // Wow, all this work for a SIMPLE TYPE with attributes?!?!?!
+ indent(out) << "<xsd:element name=\"" << name << "\"" << soptional << snillable << ">"
+ << endl;
+ indent_up();
+ indent(out) << "<xsd:complexType>" << endl;
+ indent_up();
+ indent(out) << "<xsd:complexContent>" << endl;
+ indent_up();
+ indent(out) << "<xsd:extension base=\"" << type_name(ttype) << "\">" << endl;
+ indent_up();
+ const vector<t_field*>& members = attrs->get_members();
+ vector<t_field*>::const_iterator a_iter;
+ for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) {
+ indent(out) << "<xsd:attribute name=\"" << (*a_iter)->get_name() << "\" type=\""
+ << type_name((*a_iter)->get_type()) << "\" />" << endl;
+ }
+ indent_down();
+ indent(out) << "</xsd:extension>" << endl;
+ indent_down();
+ indent(out) << "</xsd:complexContent>" << endl;
+ indent_down();
+ indent(out) << "</xsd:complexType>" << endl;
+ indent_down();
+ indent(out) << "</xsd:element>" << endl;
+ }
+ }
+}
+
+
+void t_xsd_generator::generate_service(t_service* tservice) {
+ // Make output file
+ string f_xsd_name = get_out_dir() + tservice->get_name() + ".xsd";
+ f_xsd_.open(f_xsd_name.c_str());
+
+ string ns = program_->get_namespace("xsd");
+ const std::map<std::string, std::string> annot = program_->get_namespace_annotations("xsd");
+ const std::map<std::string, std::string>::const_iterator uri = annot.find("uri");
+ if (uri != annot.end()) {
+ ns = uri->second;
+ }
+ if (ns.size() > 0) {
+ ns = " targetNamespace=\"" + ns + "\" xmlns=\"" + ns + "\" "
+ + "elementFormDefault=\"qualified\"";
+ }
+
+ // Print the XSD header
+ f_xsd_ << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl
+ << "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" << ns << ">" << endl
+ << xml_autogen_comment()
+ << endl;
+
+ // Print out the type definitions
+ indent(f_xsd_) << s_xsd_types_.str();
+
+ // Keep a list of all the possible exceptions that might get thrown
+ map<string, t_struct*> all_xceptions;
+
+ // List the elements that you might actually get
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ string elemname = (*f_iter)->get_name() + "_response";
+ t_type* returntype = (*f_iter)->get_returntype();
+ generate_element(f_xsd_, elemname, returntype);
+ f_xsd_ << endl;
+
+ t_struct* xs = (*f_iter)->get_xceptions();
+ const std::vector<t_field*>& xceptions = xs->get_members();
+ vector<t_field*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ all_xceptions[(*x_iter)->get_name()] = (t_struct*)((*x_iter)->get_type());
+ }
+ }
+
+ map<string, t_struct*>::iterator ax_iter;
+ for (ax_iter = all_xceptions.begin(); ax_iter != all_xceptions.end(); ++ax_iter) {
+ generate_element(f_xsd_, ax_iter->first, ax_iter->second);
+ }
+
+ // Close the XSD document
+ f_xsd_ << endl << "</xsd:schema>" << endl;
+ f_xsd_.close();
+}
+
+string t_xsd_generator::type_name(t_type* ttype) {
+ if (ttype->is_typedef()) {
+ return ttype->get_name();
+ }
+
+ if (ttype->is_base_type()) {
+ return xsd(base_type_name(((t_base_type*)ttype)->get_base()));
+ }
+
+ if (ttype->is_enum()) {
+ return xsd("int");
+ }
+
+ if (ttype->is_struct() || ttype->is_xception()) {
+ return ttype->get_name();
+ }
+
+ return "container";
+}
+
+/**
+ * Returns the XSD type that corresponds to the thrift type.
+ *
+ * @param tbase The base type
+ * @return Explicit XSD type, i.e. xsd:string
+ */
+string t_xsd_generator::base_type_name(t_base_type::t_base tbase) {
+ switch (tbase) {
+ case t_base_type::TYPE_VOID:
+ return "void";
+ case t_base_type::TYPE_STRING:
+ return "string";
+ case t_base_type::TYPE_BOOL:
+ return "boolean";
+ case t_base_type::TYPE_I8:
+ return "byte";
+ case t_base_type::TYPE_I16:
+ return "short";
+ case t_base_type::TYPE_I32:
+ return "int";
+ case t_base_type::TYPE_I64:
+ return "long";
+ case t_base_type::TYPE_DOUBLE:
+ return "decimal";
+ default:
+ throw "compiler error: no XSD base type name for base type " + t_base_type::t_base_name(tbase);
+ }
+}
+
+THRIFT_REGISTER_GENERATOR(xsd, "XSD", "")
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/thrift-t_php_generator.o-a60a38e9 b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/thrift-t_php_generator.o-a60a38e9
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/thrift-t_php_generator.o-a60a38e9
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/globals.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/globals.h
new file mode 100644
index 000000000..961c6ef8a
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/globals.h
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_GLOBALS_H
+#define T_GLOBALS_H
+
+#include <set>
+#include <queue>
+#include <stack>
+#include <vector>
+#include <string>
+
+/**
+ * This module contains all the global variables (slap on the wrist) that are
+ * shared throughout the program. The reason for this is to facilitate simple
+ * interaction between the parser and the rest of the program. Before calling
+ * yyparse(), the main.cc program will make necessary adjustments to these
+ * global variables such that the parser does the right thing and puts entries
+ * into the right containers, etc.
+ *
+ */
+
+/**
+ * Hooray for forward declaration of types!
+ */
+
+class t_program;
+class t_scope;
+class t_type;
+
+/**
+ * Parsing mode, two passes up in this gin rummy!
+ */
+
+enum PARSE_MODE { INCLUDES = 1, PROGRAM = 2 };
+
+/**
+ * Strictness level
+ */
+extern int g_strict;
+
+/**
+ * The master program parse tree. This is accessed from within the parser code
+ * to build up the program elements.
+ */
+extern t_program* g_program;
+
+/**
+ * The scope that we are currently parsing into
+ */
+extern t_scope* g_scope;
+
+/**
+ * The parent scope to also load symbols into
+ */
+extern t_scope* g_parent_scope;
+
+/**
+ * The prefix for the parent scope entries
+ */
+extern std::string g_parent_prefix;
+
+/**
+ * The parsing pass that we are on. We do different things on each pass.
+ */
+extern PARSE_MODE g_parse_mode;
+
+/**
+ * Global time string, used in formatting error messages etc.
+ */
+extern char* g_time_str;
+
+/**
+ * The last parsed doctext comment.
+ */
+extern char* g_doctext;
+
+/**
+ * The location of the last parsed doctext comment.
+ */
+extern int g_doctext_lineno;
+
+/**
+ * Status of program level doctext candidate
+ */
+enum PROGDOCTEXT_STATUS {
+ INVALID = 0,
+ STILL_CANDIDATE = 1, // the text may or may not be the program doctext
+ ALREADY_PROCESSED = 2, // doctext has been used and is no longer available
+ ABSOLUTELY_SURE = 3, // this is the program doctext
+ NO_PROGRAM_DOCTEXT = 4 // there is no program doctext
+};
+
+/**
+ * The program level doctext. Stored separately to make parsing easier.
+ */
+extern char* g_program_doctext_candidate;
+extern int g_program_doctext_lineno;
+extern PROGDOCTEXT_STATUS g_program_doctext_status;
+
+/**
+ * Whether or not negative field keys are accepted.
+ *
+ * When a field does not have a user-specified key, thrift automatically
+ * assigns a negative value. However, this is fragile since changes to the
+ * file may unintentionally change the key numbering, resulting in a new
+ * protocol that is not backwards compatible.
+ *
+ * When g_allow_neg_field_keys is enabled, users can explicitly specify
+ * negative keys. This way they can write a .thrift file with explicitly
+ * specified keys that is still backwards compatible with older .thrift files
+ * that did not specify key values.
+ */
+extern int g_allow_neg_field_keys;
+
+/**
+ * Whether or not 64-bit constants will generate a warning.
+ *
+ * Some languages don't support 64-bit constants, but many do, so we can
+ * suppress this warning for projects that don't use any non-64-bit-safe
+ * languages.
+ */
+extern int g_allow_64bit_consts;
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.cc
new file mode 100644
index 000000000..4811c4ef1
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.cc
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include "thrift/logging.h"
+#include "thrift/globals.h"
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+int g_debug = 0;
+int g_warn = 1;
+int g_verbose = 0;
+
+void pdebug(const char* fmt, ...) {
+ if (g_debug == 0) {
+ return;
+ }
+ va_list args;
+ // printf("[PARSE:%d] ", yylineno);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+}
+
+void pverbose(const char* fmt, ...) {
+ if (g_verbose == 0) {
+ return;
+ }
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+void pwarning(int level, const char* fmt, ...) {
+ if (g_warn < level) {
+ return;
+ }
+ va_list args;
+ // printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+}
+
+void failure(const char* fmt, ...) {
+ va_list args;
+ // fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ printf("\n");
+ exit(1);
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.h
new file mode 100644
index 000000000..ebefbf229
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/logging.h
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_LOGGING_H
+#define T_LOGGING_H
+
+extern int g_debug;
+extern int g_warn;
+extern int g_verbose;
+
+/**
+ * Parse debugging output, used to print helpful info
+ */
+void pdebug(const char* fmt, ...);
+
+/**
+ * Parser warning
+ */
+void pwarning(int level, const char* fmt, ...);
+
+/**
+ * Print verbose output message
+ */
+void pverbose(const char* fmt, ...);
+
+/**
+ * Failure!
+ */
+void failure(const char* fmt, ...);
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/main.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/main.cc
new file mode 100644
index 000000000..03e0d6f48
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/main.cc
@@ -0,0 +1,1290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * thrift - a lightweight cross-language rpc/serialization tool
+ *
+ * This file contains the main compiler engine for Thrift, which invokes the
+ * scanner/parser to build the thrift object tree. The interface generation
+ * code for each language lives in a file by the language name under the
+ * generate/ folder, and all parse structures live in parse/
+ *
+ */
+
+#include <cassert>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string>
+#include <algorithm>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifdef _WIN32
+#include <windows.h> /* for GetFullPathName */
+#endif
+
+// Careful: must include globals first for extern definitions
+#include "thrift/common.h"
+#include "thrift/globals.h"
+
+#include "thrift/platform.h"
+#include "thrift/main.h"
+#include "thrift/parse/t_program.h"
+#include "thrift/parse/t_scope.h"
+#include "thrift/generate/t_generator.h"
+#include "thrift/audit/t_audit.h"
+
+#include "thrift/version.h"
+
+using namespace std;
+
+/**
+ * Global program tree
+ */
+t_program* g_program;
+
+/**
+ * Global scope
+ */
+t_scope* g_scope;
+
+/**
+ * Parent scope to also parse types
+ */
+t_scope* g_parent_scope;
+
+/**
+ * Prefix for putting types in parent scope
+ */
+string g_parent_prefix;
+
+/**
+ * Parsing pass
+ */
+PARSE_MODE g_parse_mode;
+
+/**
+ * Current directory of file being parsed
+ */
+string g_curdir;
+
+/**
+ * Current file being parsed
+ */
+string g_curpath;
+
+/**
+ * Search path for inclusions
+ */
+vector<string> g_incl_searchpath;
+
+/**
+ * Global debug state
+ */
+int g_debug = 0;
+
+/**
+ * Strictness level
+ */
+int g_strict = 127;
+
+/**
+ * Warning level
+ */
+int g_warn = 1;
+
+/**
+ * Verbose output
+ */
+int g_verbose = 0;
+
+/**
+ * Global time string
+ */
+char* g_time_str;
+
+/**
+ * The last parsed doctext comment.
+ */
+char* g_doctext;
+
+/**
+ * The First doctext comment
+ */
+char* g_program_doctext_candidate;
+
+/**
+ * Whether or not negative field keys are accepted.
+ */
+int g_allow_neg_field_keys;
+
+/**
+ * Whether or not 64-bit constants will generate a warning.
+ */
+int g_allow_64bit_consts = 0;
+
+/**
+ * Flags to control code generation
+ */
+bool gen_recurse = false;
+
+/**
+ * Flags to control thrift audit
+ */
+bool g_audit = false;
+
+/**
+ * Flag to control return status
+ */
+bool g_return_failure = false;
+bool g_audit_fatal = true;
+bool g_generator_failure = false;
+
+/**
+ * Win32 doesn't have realpath, so use fallback implementation in that case,
+ * otherwise this just calls through to realpath
+ */
+char* saferealpath(const char* path, char* resolved_path) {
+#ifdef _WIN32
+ char buf[MAX_PATH];
+ char* basename;
+ DWORD len = GetFullPathNameA(path, MAX_PATH, buf, &basename);
+ if (len == 0 || len > MAX_PATH - 1) {
+ strcpy(resolved_path, path);
+ } else {
+ strcpy(resolved_path, buf);
+ }
+
+ // Replace backslashes with forward slashes so the
+ // rest of the code behaves correctly.
+ size_t resolved_len = strlen(resolved_path);
+ for (size_t i = 0; i < resolved_len; i++) {
+ if (resolved_path[i] == '\\') {
+ resolved_path[i] = '/';
+ }
+ }
+ return resolved_path;
+#else
+ return realpath(path, resolved_path);
+#endif
+}
+
+bool check_is_directory(const char* dir_name) {
+#ifdef _WIN32
+ DWORD attributes = ::GetFileAttributesA(dir_name);
+ if (attributes == INVALID_FILE_ATTRIBUTES) {
+ fprintf(stderr,
+ "Output directory %s is unusable: GetLastError() = %ld\n",
+ dir_name,
+ GetLastError());
+ return false;
+ }
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) {
+ fprintf(stderr, "Output directory %s exists but is not a directory\n", dir_name);
+ return false;
+ }
+ return true;
+#else
+ struct stat sb;
+ if (stat(dir_name, &sb) < 0) {
+ fprintf(stderr, "Output directory %s is unusable: %s\n", dir_name, strerror(errno));
+ return false;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ fprintf(stderr, "Output directory %s exists but is not a directory\n", dir_name);
+ return false;
+ }
+ return true;
+#endif
+}
+
+/**
+ * Report an error to the user. This is called yyerror for historical
+ * reasons (lex and yacc expect the error reporting routine to be called
+ * this). Call this function to report any errors to the user.
+ * yyerror takes printf style arguments.
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void yyerror(const char* fmt, ...) {
+ va_list args;
+ fprintf(stderr, "[ERROR:%s:%d] (last token was '%s')\n", g_curpath.c_str(), yylineno, yytext);
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ fprintf(stderr, "\n");
+}
+
+/**
+ * Prints a debug message from the parser.
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void pdebug(const char* fmt, ...) {
+ if (g_debug == 0) {
+ return;
+ }
+ va_list args;
+ printf("[PARSE:%d] ", yylineno);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+}
+
+/**
+ * Prints a verbose output mode message
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void pverbose(const char* fmt, ...) {
+ if (g_verbose == 0) {
+ return;
+ }
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+/**
+ * Prints a warning message
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void pwarning(int level, const char* fmt, ...) {
+ if (g_warn < level) {
+ return;
+ }
+ va_list args;
+ printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ printf("\n");
+}
+
+/**
+ * Prints a failure message and exits
+ *
+ * @param fmt C format string followed by additional arguments
+ */
+void failure(const char* fmt, ...) {
+ va_list args;
+ fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ printf("\n");
+ exit(1);
+}
+
+/**
+ * Converts a string filename into a thrift program name
+ */
+string program_name(string filename) {
+ string::size_type slash = filename.rfind("/");
+ if (slash != string::npos) {
+ filename = filename.substr(slash + 1);
+ }
+ string::size_type dot = filename.rfind(".");
+ if (dot != string::npos) {
+ filename = filename.substr(0, dot);
+ }
+ return filename;
+}
+
+/**
+ * Gets the directory path of a filename
+ */
+string directory_name(string filename) {
+ string::size_type slash = filename.rfind("/");
+ // No slash, just use the current directory
+ if (slash == string::npos) {
+ return ".";
+ }
+ return filename.substr(0, slash);
+}
+
+/**
+ * Finds the appropriate file path for the given filename
+ */
+string include_file(string filename) {
+ // Absolute path? Just try that
+ if (filename[0] == '/') {
+ // Realpath!
+ char rp[THRIFT_PATH_MAX];
+ // cppcheck-suppress uninitvar
+ if (saferealpath(filename.c_str(), rp) == NULL) {
+ pwarning(0, "Cannot open include file %s\n", filename.c_str());
+ return std::string();
+ }
+
+ // Stat this file
+ struct stat finfo;
+ if (stat(rp, &finfo) == 0) {
+ return rp;
+ }
+ } else { // relative path, start searching
+ // new search path with current dir global
+ vector<string> sp = g_incl_searchpath;
+ sp.insert(sp.begin(), g_curdir);
+
+ // iterate through paths
+ vector<string>::iterator it;
+ for (it = sp.begin(); it != sp.end(); it++) {
+ string sfilename = *(it) + "/" + filename;
+
+ // Realpath!
+ char rp[THRIFT_PATH_MAX];
+ // cppcheck-suppress uninitvar
+ if (saferealpath(sfilename.c_str(), rp) == NULL) {
+ continue;
+ }
+
+ // Stat this files
+ struct stat finfo;
+ if (stat(rp, &finfo) == 0) {
+ return rp;
+ }
+ }
+ }
+
+ // Uh oh
+ pwarning(0, "Could not find include file %s\n", filename.c_str());
+ return std::string();
+}
+
+/**
+ * Clears any previously stored doctext string.
+ * Also prints a warning if we are discarding information.
+ */
+void clear_doctext() {
+ if (g_doctext != NULL) {
+ pwarning(2, "Uncaptured doctext at on line %d.", g_doctext_lineno);
+ }
+ free(g_doctext);
+ g_doctext = NULL;
+}
+
+/**
+ * Reset program doctext information after processing a file
+ */
+void reset_program_doctext_info() {
+ if (g_program_doctext_candidate != NULL) {
+ free(g_program_doctext_candidate);
+ g_program_doctext_candidate = NULL;
+ }
+ g_program_doctext_lineno = 0;
+ g_program_doctext_status = INVALID;
+ pdebug("%s", "program doctext set to INVALID");
+}
+
+/**
+ * We are sure the program doctext candidate is really the program doctext.
+ */
+void declare_valid_program_doctext() {
+ if ((g_program_doctext_candidate != NULL) && (g_program_doctext_status == STILL_CANDIDATE)) {
+ g_program_doctext_status = ABSOLUTELY_SURE;
+ pdebug("%s", "program doctext set to ABSOLUTELY_SURE");
+ } else {
+ g_program_doctext_status = NO_PROGRAM_DOCTEXT;
+ pdebug("%s", "program doctext set to NO_PROGRAM_DOCTEXT");
+ }
+}
+
+/**
+ * Cleans up text commonly found in doxygen-like comments
+ *
+ * Warning: if you mix tabs and spaces in a non-uniform way,
+ * you will get what you deserve.
+ */
+char* clean_up_doctext(char* doctext) {
+ // Convert to C++ string, and remove Windows's carriage returns.
+ string docstring = doctext;
+ docstring.erase(remove(docstring.begin(), docstring.end(), '\r'), docstring.end());
+
+ // Separate into lines.
+ vector<string> lines;
+ string::size_type pos = string::npos;
+ string::size_type last;
+ while (true) {
+ last = (pos == string::npos) ? 0 : pos + 1;
+ pos = docstring.find('\n', last);
+ if (pos == string::npos) {
+ // First bit of cleaning. If the last line is only whitespace, drop it.
+ string::size_type nonwhite = docstring.find_first_not_of(" \t", last);
+ if (nonwhite != string::npos) {
+ lines.push_back(docstring.substr(last));
+ }
+ break;
+ }
+ lines.push_back(docstring.substr(last, pos - last));
+ }
+
+ // A very profound docstring.
+ if (lines.empty()) {
+ return NULL;
+ }
+
+ // Clear leading whitespace from the first line.
+ pos = lines.front().find_first_not_of(" \t");
+ lines.front().erase(0, pos);
+
+ // If every nonblank line after the first has the same number of spaces/tabs,
+ // then a star, remove them.
+ bool have_prefix = true;
+ bool found_prefix = false;
+ string::size_type prefix_len = 0;
+ vector<string>::iterator l_iter;
+ for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) {
+ if (l_iter->empty()) {
+ continue;
+ }
+
+ pos = l_iter->find_first_not_of(" \t");
+ if (!found_prefix) {
+ if (pos != string::npos) {
+ if (l_iter->at(pos) == '*') {
+ found_prefix = true;
+ prefix_len = pos;
+ } else {
+ have_prefix = false;
+ break;
+ }
+ } else {
+ // Whitespace-only line. Truncate it.
+ l_iter->clear();
+ }
+ } else if (l_iter->size() > pos && l_iter->at(pos) == '*' && pos == prefix_len) {
+ // Business as usual.
+ } else if (pos == string::npos) {
+ // Whitespace-only line. Let's truncate it for them.
+ l_iter->clear();
+ } else {
+ // The pattern has been broken.
+ have_prefix = false;
+ break;
+ }
+ }
+
+ // If our prefix survived, delete it from every line.
+ if (have_prefix) {
+ // Get the star too.
+ prefix_len++;
+ for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) {
+ l_iter->erase(0, prefix_len);
+ }
+ }
+
+ // Now delete the minimum amount of leading whitespace from each line.
+ prefix_len = string::npos;
+ for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) {
+ if (l_iter->empty()) {
+ continue;
+ }
+ pos = l_iter->find_first_not_of(" \t");
+ if (pos != string::npos && (prefix_len == string::npos || pos < prefix_len)) {
+ prefix_len = pos;
+ }
+ }
+
+ // If our prefix survived, delete it from every line.
+ if (prefix_len != string::npos) {
+ for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) {
+ l_iter->erase(0, prefix_len);
+ }
+ }
+
+ // Remove trailing whitespace from every line.
+ for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) {
+ pos = l_iter->find_last_not_of(" \t");
+ if (pos != string::npos && pos != l_iter->length() - 1) {
+ l_iter->erase(pos + 1);
+ }
+ }
+
+ // If the first line is empty, remove it.
+ // Don't do this earlier because a lot of steps skip the first line.
+ if (lines.front().empty()) {
+ lines.erase(lines.begin());
+ }
+
+ // Now rejoin the lines and copy them back into doctext.
+ docstring.clear();
+ for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) {
+ docstring += *l_iter;
+ docstring += '\n';
+ }
+
+ // assert(docstring.length() <= strlen(doctext)); may happen, see THRIFT-1755
+ if (docstring.length() <= strlen(doctext)) {
+ strcpy(doctext, docstring.c_str());
+ } else {
+ free(doctext); // too short
+ doctext = strdup(docstring.c_str());
+ }
+ return doctext;
+}
+
+/** Set to true to debug docstring parsing */
+static bool dump_docs = false;
+
+/**
+ * Dumps docstrings to stdout
+ * Only works for top-level definitions and the whole program doc
+ * (i.e., not enum constants, struct fields, or functions.
+ */
+void dump_docstrings(t_program* program) {
+ string progdoc = program->get_doc();
+ if (!progdoc.empty()) {
+ printf("Whole program doc:\n%s\n", progdoc.c_str());
+ }
+ const vector<t_typedef*>& typedefs = program->get_typedefs();
+ vector<t_typedef*>::const_iterator t_iter;
+ for (t_iter = typedefs.begin(); t_iter != typedefs.end(); ++t_iter) {
+ t_typedef* td = *t_iter;
+ if (td->has_doc()) {
+ printf("typedef %s:\n%s\n", td->get_name().c_str(), td->get_doc().c_str());
+ }
+ }
+ const vector<t_enum*>& enums = program->get_enums();
+ vector<t_enum*>::const_iterator e_iter;
+ for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) {
+ t_enum* en = *e_iter;
+ if (en->has_doc()) {
+ printf("enum %s:\n%s\n", en->get_name().c_str(), en->get_doc().c_str());
+ }
+ }
+ const vector<t_const*>& consts = program->get_consts();
+ vector<t_const*>::const_iterator c_iter;
+ for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+ t_const* co = *c_iter;
+ if (co->has_doc()) {
+ printf("const %s:\n%s\n", co->get_name().c_str(), co->get_doc().c_str());
+ }
+ }
+ const vector<t_struct*>& structs = program->get_structs();
+ vector<t_struct*>::const_iterator s_iter;
+ for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) {
+ t_struct* st = *s_iter;
+ if (st->has_doc()) {
+ printf("struct %s:\n%s\n", st->get_name().c_str(), st->get_doc().c_str());
+ }
+ }
+ const vector<t_struct*>& xceptions = program->get_xceptions();
+ vector<t_struct*>::const_iterator x_iter;
+ for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
+ t_struct* xn = *x_iter;
+ if (xn->has_doc()) {
+ printf("xception %s:\n%s\n", xn->get_name().c_str(), xn->get_doc().c_str());
+ }
+ }
+ const vector<t_service*>& services = program->get_services();
+ vector<t_service*>::const_iterator v_iter;
+ for (v_iter = services.begin(); v_iter != services.end(); ++v_iter) {
+ t_service* sv = *v_iter;
+ if (sv->has_doc()) {
+ printf("service %s:\n%s\n", sv->get_name().c_str(), sv->get_doc().c_str());
+ }
+ }
+}
+
+/**
+ * Emits a warning on list<byte>, binary type is typically a much better choice.
+ */
+void check_for_list_of_bytes(t_type* list_elem_type) {
+ if ((g_parse_mode == PROGRAM) && (list_elem_type != NULL) && list_elem_type->is_base_type()) {
+ t_base_type* tbase = (t_base_type*)list_elem_type;
+ if (tbase->get_base() == t_base_type::TYPE_I8) {
+ pwarning(1, "Consider using the more efficient \"binary\" type instead of \"list<byte>\".");
+ }
+ }
+}
+
+static bool g_byte_warning_emitted = false;
+
+/**
+ * Emits a one-time warning on byte type, promoting the new i8 type instead
+ */
+void emit_byte_type_warning() {
+ if (!g_byte_warning_emitted) {
+ pwarning(1,
+ "The \"byte\" type is a compatibility alias for \"i8\". Use \"i8\" to emphasize the "
+ "signedness of this type.\n");
+ g_byte_warning_emitted = true;
+ }
+}
+
+/**
+ * Prints deprecation notice for old NS declarations that are no longer supported
+ * If new_form is NULL, old_form is assumed to be a language identifier, such as "cpp"
+ * If new_form is not NULL, both arguments are used exactly as given
+ */
+void error_unsupported_namespace_decl(const char* old_form, const char* new_form) {
+ const char* remainder = "";
+ if( new_form == NULL) {
+ new_form = old_form;
+ remainder = "_namespace";
+ }
+ failure("Unsupported declaration '%s%s'. Use 'namespace %s' instead.", old_form, remainder, new_form);
+}
+
+/**
+ * Prints the version number
+ */
+void version() {
+ printf("Thrift version %s\n", THRIFT_VERSION);
+}
+
+/**
+ * Display the usage message and then exit with an error code.
+ */
+void usage() {
+ fprintf(stderr, "Usage: thrift [options] file\n\n");
+ fprintf(stderr, "Use thrift -help for a list of options\n");
+ exit(1);
+}
+
+/**
+ * Diplays the help message and then exits with an error code.
+ */
+void help() {
+ fprintf(stderr, "Usage: thrift [options] file\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -version Print the compiler version\n");
+ fprintf(stderr, " -o dir Set the output directory for gen-* packages\n");
+ fprintf(stderr, " (default: current directory)\n");
+ fprintf(stderr, " -out dir Set the ouput location for generated files.\n");
+ fprintf(stderr, " (no gen-* folder will be created)\n");
+ fprintf(stderr, " -I dir Add a directory to the list of directories\n");
+ fprintf(stderr, " searched for include directives\n");
+ fprintf(stderr, " -nowarn Suppress all compiler warnings (BAD!)\n");
+ fprintf(stderr, " -strict Strict compiler warnings on\n");
+ fprintf(stderr, " -v[erbose] Verbose mode\n");
+ fprintf(stderr, " -r[ecurse] Also generate included files\n");
+ fprintf(stderr, " -debug Parse debug trace to stdout\n");
+ fprintf(stderr,
+ " --allow-neg-keys Allow negative field keys (Used to "
+ "preserve protocol\n");
+ fprintf(stderr, " compatibility with older .thrift files)\n");
+ fprintf(stderr, " --allow-64bit-consts Do not print warnings about using 64-bit constants\n");
+ fprintf(stderr, " --gen STR Generate code with a dynamically-registered generator.\n");
+ fprintf(stderr, " STR has the form language[:key1=val1[,key2[,key3=val3]]].\n");
+ fprintf(stderr, " Keys and values are options passed to the generator.\n");
+ fprintf(stderr, " Many options will not require values.\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Options related to audit operation\n");
+ fprintf(stderr, " --audit OldFile Old Thrift file to be audited with 'file'\n");
+ fprintf(stderr, " -Iold dir Add a directory to the list of directories\n");
+ fprintf(stderr, " searched for include directives for old thrift file\n");
+ fprintf(stderr, " -Inew dir Add a directory to the list of directories\n");
+ fprintf(stderr, " searched for include directives for new thrift file\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Available generators (and options):\n");
+
+ t_generator_registry::gen_map_t gen_map = t_generator_registry::get_generator_map();
+ t_generator_registry::gen_map_t::iterator iter;
+ for (iter = gen_map.begin(); iter != gen_map.end(); ++iter) {
+ fprintf(stderr,
+ " %s (%s):\n",
+ iter->second->get_short_name().c_str(),
+ iter->second->get_long_name().c_str());
+ fprintf(stderr, "%s", iter->second->get_documentation().c_str());
+ }
+ exit(1);
+}
+
+/**
+ * You know, when I started working on Thrift I really thought it wasn't going
+ * to become a programming language because it was just a generator and it
+ * wouldn't need runtime type information and all that jazz. But then we
+ * decided to add constants, and all of a sudden that means runtime type
+ * validation and inference, except the "runtime" is the code generator
+ * runtime.
+ */
+void validate_const_rec(std::string name, t_type* type, t_const_value* value) {
+ if (type->is_void()) {
+ throw "type error: cannot declare a void const: " + name;
+ }
+
+ if (type->is_base_type()) {
+ t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
+ switch (tbase) {
+ case t_base_type::TYPE_STRING:
+ if (value->get_type() != t_const_value::CV_STRING) {
+ throw "type error: const \"" + name + "\" was declared as string";
+ }
+ break;
+ case t_base_type::TYPE_BOOL:
+ if (value->get_type() != t_const_value::CV_INTEGER) {
+ throw "type error: const \"" + name + "\" was declared as bool";
+ }
+ break;
+ case t_base_type::TYPE_I8:
+ if (value->get_type() != t_const_value::CV_INTEGER) {
+ throw "type error: const \"" + name + "\" was declared as byte";
+ }
+ break;
+ case t_base_type::TYPE_I16:
+ if (value->get_type() != t_const_value::CV_INTEGER) {
+ throw "type error: const \"" + name + "\" was declared as i16";
+ }
+ break;
+ case t_base_type::TYPE_I32:
+ if (value->get_type() != t_const_value::CV_INTEGER) {
+ throw "type error: const \"" + name + "\" was declared as i32";
+ }
+ break;
+ case t_base_type::TYPE_I64:
+ if (value->get_type() != t_const_value::CV_INTEGER) {
+ throw "type error: const \"" + name + "\" was declared as i64";
+ }
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ if (value->get_type() != t_const_value::CV_INTEGER
+ && value->get_type() != t_const_value::CV_DOUBLE) {
+ throw "type error: const \"" + name + "\" was declared as double";
+ }
+ break;
+ default:
+ throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase) + name;
+ }
+ } else if (type->is_enum()) {
+ if (value->get_type() != t_const_value::CV_IDENTIFIER) {
+ throw "type error: const \"" + name + "\" was declared as enum";
+ }
+
+ // see if there's a dot in the identifier
+ std::string name_portion = value->get_identifier_name();
+
+ const vector<t_enum_value*>& enum_values = ((t_enum*)type)->get_constants();
+ vector<t_enum_value*>::const_iterator c_iter;
+ bool found = false;
+
+ for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
+ if ((*c_iter)->get_name() == name_portion) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw "type error: const " + name + " was declared as type " + type->get_name()
+ + " which is an enum, but " + value->get_identifier()
+ + " is not a valid value for that enum";
+ }
+ } else if (type->is_struct() || type->is_xception()) {
+ if (value->get_type() != t_const_value::CV_MAP) {
+ throw "type error: const \"" + name + "\" was declared as struct/xception";
+ }
+ const vector<t_field*>& fields = ((t_struct*)type)->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ if (v_iter->first->get_type() != t_const_value::CV_STRING) {
+ throw "type error: " + name + " struct key must be string";
+ }
+ t_type* field_type = NULL;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if ((*f_iter)->get_name() == v_iter->first->get_string()) {
+ field_type = (*f_iter)->get_type();
+ }
+ }
+ if (field_type == NULL) {
+ throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
+ }
+
+ validate_const_rec(name + "." + v_iter->first->get_string(), field_type, v_iter->second);
+ }
+ } else if (type->is_map()) {
+ t_type* k_type = ((t_map*)type)->get_key_type();
+ t_type* v_type = ((t_map*)type)->get_val_type();
+ const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
+ map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ validate_const_rec(name + "<key>", k_type, v_iter->first);
+ validate_const_rec(name + "<val>", v_type, v_iter->second);
+ }
+ } else if (type->is_list() || type->is_set()) {
+ t_type* e_type;
+ if (type->is_list()) {
+ e_type = ((t_list*)type)->get_elem_type();
+ } else {
+ e_type = ((t_set*)type)->get_elem_type();
+ }
+ const vector<t_const_value*>& val = value->get_list();
+ vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ validate_const_rec(name + "<elem>", e_type, *v_iter);
+ }
+ }
+}
+
+/**
+ * Check simple identifier names
+ * It's easier to do it this way instead of rewriting the whole grammar etc.
+ */
+void validate_simple_identifier(const char* identifier) {
+ string name(identifier);
+ if (name.find(".") != string::npos) {
+ yyerror("Identifier %s can't have a dot.", identifier);
+ exit(1);
+ }
+}
+
+/**
+ * Check the type of the parsed const information against its declared type
+ */
+void validate_const_type(t_const* c) {
+ validate_const_rec(c->get_name(), c->get_type(), c->get_value());
+}
+
+/**
+ * Check the type of a default value assigned to a field.
+ */
+void validate_field_value(t_field* field, t_const_value* cv) {
+ validate_const_rec(field->get_name(), field->get_type(), cv);
+}
+
+/**
+ * Check that all the elements of a throws block are actually exceptions.
+ */
+bool validate_throws(t_struct* throws) {
+ const vector<t_field*>& members = throws->get_members();
+ vector<t_field*>::const_iterator m_iter;
+ for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+ if (!t_generator::get_true_type((*m_iter)->get_type())->is_xception()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Skips UTF-8 BOM if there is one
+ */
+bool skip_utf8_bom(FILE* f) {
+
+ // pretty straightforward, but works
+ if (fgetc(f) == 0xEF) {
+ if (fgetc(f) == 0xBB) {
+ if (fgetc(f) == 0xBF) {
+ return true;
+ }
+ }
+ }
+
+ rewind(f);
+ return false;
+}
+
+/**
+ * Parses a program
+ */
+void parse(t_program* program, t_program* parent_program) {
+ // Get scope file path
+ string path = program->get_path();
+
+ // Set current dir global, which is used in the include_file function
+ g_curdir = directory_name(path);
+ g_curpath = path;
+
+ // Open the file
+ // skip UTF-8 BOM if there is one
+ yyin = fopen(path.c_str(), "r");
+ if (yyin == 0) {
+ failure("Could not open input file: \"%s\"", path.c_str());
+ }
+ if (skip_utf8_bom(yyin))
+ pverbose("Skipped UTF-8 BOM at %s\n", path.c_str());
+
+ // Create new scope and scan for includes
+ pverbose("Scanning %s for includes\n", path.c_str());
+ g_parse_mode = INCLUDES;
+ g_program = program;
+ g_scope = program->scope();
+ try {
+ yylineno = 1;
+ if (yyparse() != 0) {
+ failure("Parser error during include pass.");
+ }
+ } catch (string x) {
+ failure(x.c_str());
+ }
+ fclose(yyin);
+
+ // Recursively parse all the include programs
+ vector<t_program*>& includes = program->get_includes();
+ vector<t_program*>::iterator iter;
+ for (iter = includes.begin(); iter != includes.end(); ++iter) {
+ parse(*iter, program);
+ }
+
+ // reset program doctext status before parsing a new file
+ reset_program_doctext_info();
+
+ // Parse the program file
+ g_parse_mode = PROGRAM;
+ g_program = program;
+ g_scope = program->scope();
+ g_parent_scope = (parent_program != NULL) ? parent_program->scope() : NULL;
+ g_parent_prefix = program->get_name() + ".";
+ g_curpath = path;
+
+ // Open the file
+ // skip UTF-8 BOM if there is one
+ yyin = fopen(path.c_str(), "r");
+ if (yyin == 0) {
+ failure("Could not open input file: \"%s\"", path.c_str());
+ }
+ if (skip_utf8_bom(yyin))
+ pverbose("Skipped UTF-8 BOM at %s\n", path.c_str());
+
+ pverbose("Parsing %s for types\n", path.c_str());
+ yylineno = 1;
+ try {
+ if (yyparse() != 0) {
+ failure("Parser error during types pass.");
+ }
+ } catch (string x) {
+ failure(x.c_str());
+ }
+ fclose(yyin);
+}
+
+/**
+ * Generate code
+ */
+void generate(t_program* program, const vector<string>& generator_strings) {
+ // Oooohh, recursive code generation, hot!!
+ if (gen_recurse) {
+ program->set_recursive(true);
+ const vector<t_program*>& includes = program->get_includes();
+ for (auto include : includes) {
+ // Propagate output path from parent to child programs
+ include->set_out_path(program->get_out_path(), program->is_out_path_absolute());
+
+ generate(include, generator_strings);
+ }
+ }
+
+ // Generate code!
+ try {
+ pverbose("Program: %s\n", program->get_path().c_str());
+
+ if (dump_docs) {
+ dump_docstrings(program);
+ }
+
+ vector<string>::const_iterator iter;
+ for (iter = generator_strings.begin(); iter != generator_strings.end(); ++iter) {
+ t_generator* generator = t_generator_registry::get_generator(program, *iter);
+
+ if (generator == NULL) {
+ pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str());
+ g_generator_failure = true;
+ } else if (generator) {
+ generator->validate_input();
+ pverbose("Generating \"%s\"\n", iter->c_str());
+ generator->generate_program();
+ delete generator;
+ }
+ }
+ } catch (string s) {
+ failure("Error: %s\n", s.c_str());
+ } catch (const char* exc) {
+ failure("Error: %s\n", exc);
+ } catch (const std::invalid_argument& invalid_argument_exception) {
+ failure("Error: %s\n", invalid_argument_exception.what());
+ }
+}
+
+void audit(t_program* new_program,
+ t_program* old_program,
+ string new_thrift_include_path,
+ string old_thrift_include_path) {
+ vector<string> temp_incl_searchpath = g_incl_searchpath;
+ if (!old_thrift_include_path.empty()) {
+ g_incl_searchpath.push_back(old_thrift_include_path);
+ }
+
+ parse(old_program, NULL);
+
+ g_incl_searchpath = temp_incl_searchpath;
+ if (!new_thrift_include_path.empty()) {
+ g_incl_searchpath.push_back(new_thrift_include_path);
+ }
+
+ parse(new_program, NULL);
+
+ compare_namespace(new_program, old_program);
+ compare_services(new_program->get_services(), old_program->get_services());
+ compare_enums(new_program->get_enums(), old_program->get_enums());
+ compare_structs(new_program->get_structs(), old_program->get_structs());
+ compare_structs(new_program->get_xceptions(), old_program->get_xceptions());
+ compare_consts(new_program->get_consts(), old_program->get_consts());
+}
+
+/**
+ * Parse it up.. then spit it back out, in pretty much every language. Alright
+ * not that many languages, but the cool ones that we care about.
+ */
+int main(int argc, char** argv) {
+ int i;
+ std::string out_path;
+ bool out_path_is_absolute = false;
+
+ // Setup time string
+ time_t now = time(NULL);
+ g_time_str = ctime(&now);
+
+ // Check for necessary arguments, you gotta have at least a filename and
+ // an output language flag
+ if (argc < 2) {
+ usage();
+ }
+
+ vector<string> generator_strings;
+ string old_thrift_include_path;
+ string new_thrift_include_path;
+ string old_input_file;
+
+ // Set the current path to a dummy value to make warning messages clearer.
+ g_curpath = "arguments";
+
+ // Hacky parameter handling... I didn't feel like using a library sorry!
+ for (i = 1; i < argc - 1; i++) {
+ char* arg;
+
+ arg = strtok(argv[i], " ");
+ while (arg != NULL) {
+ // Treat double dashes as single dashes
+ if (arg[0] == '-' && arg[1] == '-') {
+ ++arg;
+ }
+
+ if (strcmp(arg, "-help") == 0) {
+ help();
+ } else if (strcmp(arg, "-version") == 0) {
+ version();
+ exit(0);
+ } else if (strcmp(arg, "-debug") == 0) {
+ g_debug = 1;
+ } else if (strcmp(arg, "-nowarn") == 0) {
+ g_warn = 0;
+ } else if (strcmp(arg, "-strict") == 0) {
+ g_strict = 255;
+ g_warn = 2;
+ } else if (strcmp(arg, "-v") == 0 || strcmp(arg, "-verbose") == 0) {
+ g_verbose = 1;
+ } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0) {
+ gen_recurse = true;
+ } else if (strcmp(arg, "-allow-neg-keys") == 0) {
+ g_allow_neg_field_keys = true;
+ } else if (strcmp(arg, "-allow-64bit-consts") == 0) {
+ g_allow_64bit_consts = true;
+ } else if (strcmp(arg, "-gen") == 0) {
+ arg = argv[++i];
+ if (arg == NULL) {
+ fprintf(stderr, "Missing generator specification\n");
+ usage();
+ }
+ generator_strings.emplace_back(arg);
+ } else if (strcmp(arg, "-I") == 0) {
+ // An argument of "-I\ asdf" is invalid and has unknown results
+ arg = argv[++i];
+
+ if (arg == NULL) {
+ fprintf(stderr, "Missing Include directory\n");
+ usage();
+ }
+ g_incl_searchpath.emplace_back(arg);
+ } else if ((strcmp(arg, "-o") == 0) || (strcmp(arg, "-out") == 0)) {
+ out_path_is_absolute = (strcmp(arg, "-out") == 0) ? true : false;
+ arg = argv[++i];
+ if (arg == NULL) {
+ fprintf(stderr, "-o: missing output directory\n");
+ usage();
+ }
+ out_path = arg;
+
+#ifdef _WIN32
+ // strip out trailing \ on Windows
+ std::string::size_type last = out_path.length() - 1;
+ if (out_path[last] == '\\') {
+ out_path.erase(last);
+ }
+#endif
+ if (!check_is_directory(out_path.c_str()))
+ return -1;
+ } else if (strcmp(arg, "-audit") == 0) {
+ g_audit = true;
+ arg = argv[++i];
+ if (arg == NULL) {
+ fprintf(stderr, "Missing old thrift file name for audit operation\n");
+ usage();
+ }
+ char old_thrift_file_rp[THRIFT_PATH_MAX];
+
+ // cppcheck-suppress uninitvar
+ if (saferealpath(arg, old_thrift_file_rp) == NULL) {
+ failure("Could not open input file with realpath: %s", arg);
+ }
+ old_input_file = string(old_thrift_file_rp);
+ } else if (strcmp(arg, "-audit-nofatal") == 0) {
+ g_audit_fatal = false;
+ } else if (strcmp(arg, "-Iold") == 0) {
+ arg = argv[++i];
+ if (arg == NULL) {
+ fprintf(stderr, "Missing Include directory for old thrift file\n");
+ usage();
+ }
+ old_thrift_include_path = string(arg);
+ } else if (strcmp(arg, "-Inew") == 0) {
+ arg = argv[++i];
+ if (arg == NULL) {
+ fprintf(stderr, "Missing Include directory for new thrift file\n");
+ usage();
+ }
+ new_thrift_include_path = string(arg);
+ } else {
+ fprintf(stderr, "Unrecognized option: %s\n", arg);
+ usage();
+ }
+
+ // Tokenize more
+ arg = strtok(NULL, " ");
+ }
+ }
+
+ // display help
+ if ((strcmp(argv[argc - 1], "-help") == 0) || (strcmp(argv[argc - 1], "--help") == 0)) {
+ help();
+ }
+
+ // if you're asking for version, you have a right not to pass a file
+ if ((strcmp(argv[argc - 1], "-version") == 0) || (strcmp(argv[argc - 1], "--version") == 0)) {
+ version();
+ exit(0);
+ }
+
+ // Initialize global types
+ initGlobals();
+
+ if (g_audit) {
+ // Audit operation
+
+ if (old_input_file.empty()) {
+ fprintf(stderr, "Missing file name of old thrift file for audit\n");
+ usage();
+ }
+
+ char new_thrift_file_rp[THRIFT_PATH_MAX];
+ if (argv[i] == NULL) {
+ fprintf(stderr, "Missing file name of new thrift file for audit\n");
+ usage();
+ }
+ // cppcheck-suppress uninitvar
+ if (saferealpath(argv[i], new_thrift_file_rp) == NULL) {
+ failure("Could not open input file with realpath: %s", argv[i]);
+ }
+ string new_input_file(new_thrift_file_rp);
+
+ t_program new_program(new_input_file);
+ t_program old_program(old_input_file);
+
+ audit(&new_program, &old_program, new_thrift_include_path, old_thrift_include_path);
+
+ } else {
+ // Generate options
+
+ // You gotta generate something!
+ if (generator_strings.empty()) {
+ fprintf(stderr, "No output language(s) specified\n");
+ usage();
+ }
+
+ // Real-pathify it
+ char rp[THRIFT_PATH_MAX];
+ if (argv[i] == NULL) {
+ fprintf(stderr, "Missing file name\n");
+ usage();
+ }
+ // cppcheck-suppress uninitvar
+ if (saferealpath(argv[i], rp) == NULL) {
+ failure("Could not open input file with realpath: %s", argv[i]);
+ }
+ string input_file(rp);
+
+ // Instance of the global parse tree
+ t_program* program = new t_program(input_file);
+ if (out_path.size()) {
+ program->set_out_path(out_path, out_path_is_absolute);
+ }
+
+ // Compute the cpp include prefix.
+ // infer this from the filename passed in
+ string input_filename = argv[i];
+ string include_prefix;
+
+ string::size_type last_slash = string::npos;
+ if ((last_slash = input_filename.rfind("/")) != string::npos) {
+ include_prefix = input_filename.substr(0, last_slash);
+ }
+
+ program->set_include_prefix(include_prefix);
+
+ // Parse it!
+ parse(program, NULL);
+
+ // The current path is not really relevant when we are doing generation.
+ // Reset the variable to make warning messages clearer.
+ g_curpath = "generation";
+ // Reset yylineno for the heck of it. Use 1 instead of 0 because
+ // That is what shows up during argument parsing.
+ yylineno = 1;
+
+ // Generate it!
+ generate(program, generator_strings);
+ delete program;
+ }
+
+ // Clean up. Who am I kidding... this program probably orphans heap memory
+ // all over the place, but who cares because it is about to exit and it is
+ // all referenced and used by this wacky parse tree up until now anyways.
+ clearGlobals();
+
+ // Finished
+ if (g_return_failure && g_audit_fatal) {
+ exit(2);
+ }
+ if (g_generator_failure) {
+ exit(3);
+ }
+ // Finished
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/main.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/main.h
new file mode 100644
index 000000000..163b02e4b
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/main.h
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_MAIN_H
+#define T_MAIN_H
+
+#include <string>
+#include <cstdio>
+
+#include "thrift/logging.h"
+
+#include "thrift/parse/t_const.h"
+#include "thrift/parse/t_field.h"
+
+/**
+ * Defined in the flex library
+ */
+
+extern "C" { int yylex(void); }
+
+int yyparse(void);
+
+/**
+ * Expected to be defined by Flex/Bison
+ */
+void yyerror(const char* fmt, ...);
+
+/**
+ * Check simple identifier names
+ */
+void validate_simple_identifier(const char* identifier);
+
+/**
+ * Check constant types
+ */
+void validate_const_type(t_const* c);
+
+/**
+ * Check constant types
+ */
+void validate_field_value(t_field* field, t_const_value* cv);
+
+/**
+ * Check members of a throws block
+ */
+bool validate_throws(t_struct* throws);
+
+/**
+ * Converts a string filename into a thrift program name
+ */
+std::string program_name(std::string filename);
+
+/**
+ * Gets the directory path of a filename
+ */
+std::string directory_name(std::string filename);
+
+/**
+ * Get the absolute path for an include file
+ */
+std::string include_file(std::string filename);
+
+/**
+ * Clears any previously stored doctext string.
+ */
+void clear_doctext();
+
+/**
+ * Cleans up text commonly found in doxygen-like comments
+ */
+char* clean_up_doctext(char* doctext);
+
+/**
+ * We are sure the program doctext candidate is really the program doctext.
+ */
+void declare_valid_program_doctext();
+
+/**
+ * Emits a warning on list<byte>, binary type is typically a much better choice.
+ */
+void check_for_list_of_bytes(t_type* list_elem_type);
+
+/**
+ * Emits a one-time warning on byte type, promoting the new i8 type instead
+ */
+void emit_byte_type_warning();
+
+/**
+ * Prints deprecation notice for old NS declarations that are no longer supported
+ * If new_form is NULL, old_form is assumed to be a language identifier, such as "cpp"
+ * If new_form is not NULL, both arguments are used exactly as given
+ */
+void error_unsupported_namespace_decl(const char* old_form, const char* new_form = nullptr);
+
+/**
+ * Flex utilities
+ */
+
+extern int yylineno;
+extern char yytext[];
+extern std::FILE* yyin;
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/parse.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/parse.cc
new file mode 100644
index 000000000..23db61ccb
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/parse.cc
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include "thrift/parse/t_type.h"
+#include "thrift/parse/t_typedef.h"
+
+#include "thrift/main.h"
+
+t_type* t_type::get_true_type() {
+ return const_cast<t_type*>(const_cast<const t_type*>(this)->get_true_type());
+}
+
+const t_type* t_type::get_true_type() const {
+ const t_type* type = this;
+ while (type->is_typedef()) {
+ type = ((t_typedef*)type)->get_type();
+ }
+ return type;
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_base_type.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_base_type.h
new file mode 100644
index 000000000..ca2b0f6ef
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_base_type.h
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_BASE_TYPE_H
+#define T_BASE_TYPE_H
+
+#include <cstdlib>
+#include "thrift/parse/t_type.h"
+
+/**
+ * A thrift base type, which must be one of the defined enumerated types inside
+ * this definition.
+ *
+ */
+class t_base_type : public t_type {
+public:
+ /**
+ * Enumeration of thrift base types
+ */
+ enum t_base {
+ TYPE_VOID,
+ TYPE_STRING,
+ TYPE_BOOL,
+ TYPE_I8,
+ TYPE_I16,
+ TYPE_I32,
+ TYPE_I64,
+ TYPE_DOUBLE
+ };
+
+ t_base_type(std::string name, t_base base)
+ : t_type(name), base_(base), string_list_(false), binary_(false), string_enum_(false) {}
+
+ t_base get_base() const { return base_; }
+
+ bool is_void() const override { return base_ == TYPE_VOID; }
+
+ bool is_string() const override { return base_ == TYPE_STRING; }
+
+ bool is_bool() const override { return base_ == TYPE_BOOL; }
+
+ void set_string_list(bool val) { string_list_ = val; }
+
+ bool is_string_list() const { return string_list_ && (base_ == TYPE_STRING); }
+
+ void set_binary(bool val) { binary_ = val; }
+
+ bool is_binary() const override { return binary_ && (base_ == TYPE_STRING); }
+
+ void set_string_enum(bool val) { string_enum_ = val; }
+
+ bool is_string_enum() const { return string_enum_ && base_ == TYPE_STRING; }
+
+ void add_string_enum_val(std::string val) { string_enum_vals_.push_back(val); }
+
+ const std::vector<std::string>& get_string_enum_vals() const { return string_enum_vals_; }
+
+ bool is_base_type() const override { return true; }
+
+ static std::string t_base_name(t_base tbase) {
+ switch (tbase) {
+ case TYPE_VOID:
+ return "void";
+ break;
+ case TYPE_STRING:
+ return "string";
+ break;
+ case TYPE_BOOL:
+ return "bool";
+ break;
+ case TYPE_I8:
+ return "i8";
+ break;
+ case TYPE_I16:
+ return "i16";
+ break;
+ case TYPE_I32:
+ return "i32";
+ break;
+ case TYPE_I64:
+ return "i64";
+ break;
+ case TYPE_DOUBLE:
+ return "double";
+ break;
+ default:
+ return "(unknown)";
+ break;
+ }
+ }
+
+private:
+ t_base base_;
+
+ bool string_list_;
+ bool binary_;
+ bool string_enum_;
+ std::vector<std::string> string_enum_vals_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const.h
new file mode 100644
index 000000000..3c08e0dec
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const.h
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_CONST_H
+#define T_CONST_H
+
+#include "thrift/parse/t_type.h"
+#include "thrift/parse/t_const_value.h"
+
+/**
+ * A const is a constant value defined across languages that has a type and
+ * a value. The trick here is that the declared type might not match the type
+ * of the value object, since that is not determined until after parsing the
+ * whole thing out.
+ *
+ */
+class t_const : public t_doc {
+public:
+ t_const(t_type* type, std::string name, t_const_value* value)
+ : type_(type), name_(name), value_(value) {}
+
+ t_type* get_type() const { return type_; }
+
+ std::string get_name() const { return name_; }
+
+ t_const_value* get_value() const { return value_; }
+
+private:
+ t_type* type_;
+ std::string name_;
+ t_const_value* value_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const_value.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const_value.h
new file mode 100644
index 000000000..5b8156f1a
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_const_value.h
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_CONST_VALUE_H
+#define T_CONST_VALUE_H
+
+#include "thrift/parse/t_enum.h"
+#include <stdint.h>
+#include <map>
+#include <vector>
+#include <string>
+
+/**
+ * A const value is something parsed that could be a map, set, list, struct
+ * or whatever.
+ *
+ */
+class t_const_value {
+public:
+ /**
+ * Comparator to sort fields in ascending order by key.
+ * Make this a functor instead of a function to help GCC inline it.
+ */
+ struct value_compare {
+ public:
+ bool operator()(t_const_value const* const& left, t_const_value const* const& right) const {
+ return *left < *right;
+ }
+ };
+
+ enum t_const_value_type { CV_INTEGER, CV_DOUBLE, CV_STRING, CV_MAP, CV_LIST, CV_IDENTIFIER, CV_UNKNOWN };
+
+ t_const_value() : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) {}
+
+ t_const_value(int64_t val) : doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) { set_integer(val); }
+
+ t_const_value(std::string val) : intVal_(0), doubleVal_(0.0f), enum_((t_enum*)nullptr), valType_(CV_UNKNOWN) { set_string(val); }
+
+ void set_string(std::string val) {
+ valType_ = CV_STRING;
+ stringVal_ = val;
+ }
+
+ std::string get_string() const { return stringVal_; }
+
+ void set_integer(int64_t val) {
+ valType_ = CV_INTEGER;
+ intVal_ = val;
+ }
+
+ int64_t get_integer() const {
+ if (valType_ == CV_IDENTIFIER) {
+ if (enum_ == nullptr) {
+ throw "have identifier \"" + get_identifier() + "\", but unset enum on line!";
+ }
+ std::string identifier = get_identifier();
+ std::string::size_type dot = identifier.rfind('.');
+ if (dot != std::string::npos) {
+ identifier = identifier.substr(dot + 1);
+ }
+ t_enum_value* val = enum_->get_constant_by_name(identifier);
+ if (val == nullptr) {
+ throw "Unable to find enum value \"" + identifier + "\" in enum \"" + enum_->get_name()
+ + "\"";
+ }
+ return val->get_value();
+ } else {
+ return intVal_;
+ }
+ }
+
+ void set_double(double val) {
+ valType_ = CV_DOUBLE;
+ doubleVal_ = val;
+ }
+
+ double get_double() const { return doubleVal_; }
+
+ void set_map() { valType_ = CV_MAP; }
+
+ void add_map(t_const_value* key, t_const_value* val) { mapVal_[key] = val; }
+
+ const std::map<t_const_value*, t_const_value*, t_const_value::value_compare>& get_map() const { return mapVal_; }
+
+ void set_list() { valType_ = CV_LIST; }
+
+ void add_list(t_const_value* val) { listVal_.push_back(val); }
+
+ const std::vector<t_const_value*>& get_list() const { return listVal_; }
+
+ void set_identifier(std::string val) {
+ valType_ = CV_IDENTIFIER;
+ identifierVal_ = val;
+ }
+
+ std::string get_identifier() const { return identifierVal_; }
+
+ std::string get_identifier_name() const {
+ std::string ret = get_identifier();
+ size_t s = ret.find('.');
+ if (s == std::string::npos) {
+ throw "error: identifier " + ret + " is unqualified!";
+ }
+ ret = ret.substr(s + 1);
+ s = ret.find('.');
+ if (s != std::string::npos) {
+ ret = ret.substr(s + 1);
+ }
+ return ret;
+ }
+
+ std::string get_identifier_with_parent() const {
+ std::string ret = get_identifier();
+ size_t s = ret.find('.');
+ if (s == std::string::npos) {
+ throw "error: identifier " + ret + " is unqualified!";
+ }
+ size_t s2 = ret.find('.', s + 1);
+ if (s2 != std::string::npos) {
+ ret = ret.substr(s + 1);
+ }
+ return ret;
+ }
+
+ void set_enum(t_enum* tenum) { enum_ = tenum; }
+
+ t_const_value_type get_type() const { if (valType_ == CV_UNKNOWN) { throw std::string("unknown t_const_value"); } return valType_; }
+
+ /**
+ * Comparator to sort map fields in ascending order by key and then value.
+ * This is used for map comparison in lexicographic order.
+ */
+ struct map_entry_compare {
+ private:
+ typedef std::pair<t_const_value*, t_const_value*> ConstPair;
+ public:
+ bool operator()(ConstPair left, ConstPair right) const {
+ if (*(left.first) < *(right.first)) {
+ return true;
+ } else {
+ if (*(right.first) < *(left.first)) {
+ return false;
+ } else {
+ return *(left.second) < *(right.second);
+ }
+ }
+ }
+ };
+
+ bool operator < (const t_const_value& that) const {
+ ::t_const_value::t_const_value_type t1 = get_type();
+ ::t_const_value::t_const_value_type t2 = that.get_type();
+ if (t1 != t2)
+ return t1 < t2;
+ switch (t1) {
+ case ::t_const_value::CV_INTEGER:
+ return intVal_ < that.intVal_;
+ case ::t_const_value::CV_DOUBLE:
+ return doubleVal_ < that.doubleVal_;
+ case ::t_const_value::CV_STRING:
+ return stringVal_ < that.stringVal_;
+ case ::t_const_value::CV_IDENTIFIER:
+ return identifierVal_ < that.identifierVal_;
+ case ::t_const_value::CV_MAP:
+ return std::lexicographical_compare(
+ mapVal_.begin(), mapVal_.end(), that.mapVal_.begin(), that.mapVal_.end(), map_entry_compare());
+ case ::t_const_value::CV_LIST:
+ return std::lexicographical_compare(
+ listVal_.begin(), listVal_.end(), that.listVal_.begin(), that.listVal_.end(), value_compare());
+ case ::t_const_value::CV_UNKNOWN:
+ default:
+ throw "unknown value type";
+ }
+ }
+
+private:
+ std::map<t_const_value*, t_const_value*, value_compare> mapVal_;
+ std::vector<t_const_value*> listVal_;
+ std::string stringVal_;
+ int64_t intVal_;
+ double doubleVal_;
+ std::string identifierVal_;
+ t_enum* enum_;
+
+ t_const_value_type valType_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_container.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_container.h
new file mode 100644
index 000000000..a124d31fd
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_container.h
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_CONTAINER_H
+#define T_CONTAINER_H
+
+#include "thrift/parse/t_type.h"
+
+class t_container : public t_type {
+public:
+ t_container() : cpp_name_(), has_cpp_name_(false) {}
+
+ ~t_container() override = default;
+
+ void set_cpp_name(std::string cpp_name) {
+ cpp_name_ = cpp_name;
+ has_cpp_name_ = true;
+ }
+
+ bool has_cpp_name() const { return has_cpp_name_; }
+
+ std::string get_cpp_name() const { return cpp_name_; }
+
+ bool is_container() const override { return true; }
+
+private:
+ std::string cpp_name_;
+ bool has_cpp_name_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_doc.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_doc.h
new file mode 100644
index 000000000..0df893eb2
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_doc.h
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_DOC_H
+#define T_DOC_H
+
+#include "thrift/globals.h"
+#include "thrift/logging.h"
+
+/**
+ * Documentation stubs
+ *
+ */
+class t_doc {
+
+public:
+ t_doc() : has_doc_(false) {}
+ virtual ~t_doc() = default;
+
+ void set_doc(const std::string& doc) {
+ doc_ = doc;
+ has_doc_ = true;
+ if ((g_program_doctext_lineno == g_doctext_lineno)
+ && (g_program_doctext_status == STILL_CANDIDATE)) {
+ g_program_doctext_status = ALREADY_PROCESSED;
+ pdebug("%s", "program doctext set to ALREADY_PROCESSED");
+ }
+ }
+
+ const std::string& get_doc() const { return doc_; }
+
+ bool has_doc() { return has_doc_; }
+
+private:
+ std::string doc_;
+ bool has_doc_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum.h
new file mode 100644
index 000000000..3f013ee1f
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum.h
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_ENUM_H
+#define T_ENUM_H
+
+#include <vector>
+
+#include "thrift/parse/t_enum_value.h"
+#include "thrift/parse/t_type.h"
+
+/**
+ * An enumerated type. A list of constant objects with a name for the type.
+ *
+ */
+class t_enum : public t_type {
+public:
+ t_enum(t_program* program) : t_type(program) {}
+
+ void set_name(const std::string& name) override { name_ = name; }
+
+ void append(t_enum_value* constant) { constants_.push_back(constant); }
+
+ const std::vector<t_enum_value*>& get_constants() const { return constants_; }
+
+ t_enum_value* get_constant_by_name(const std::string& name) const {
+ const std::vector<t_enum_value*>& enum_values = get_constants();
+ std::vector<t_enum_value*>::const_iterator c_iter;
+ for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
+ if ((*c_iter)->get_name() == name) {
+ return *c_iter;
+ }
+ }
+ return nullptr;
+ }
+
+ t_enum_value* get_constant_by_value(int64_t value) const {
+ const std::vector<t_enum_value*>& enum_values = get_constants();
+ std::vector<t_enum_value*>::const_iterator c_iter;
+ for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
+ if ((*c_iter)->get_value() == value) {
+ return *c_iter;
+ }
+ }
+ return nullptr;
+ }
+
+ t_enum_value* get_min_value() const {
+ const std::vector<t_enum_value*>& enum_values = get_constants();
+ std::vector<t_enum_value*>::const_iterator c_iter;
+ t_enum_value* min_value;
+ if (enum_values.size() == 0) {
+ min_value = nullptr;
+ } else {
+ int min_value_value;
+ min_value = enum_values.front();
+ min_value_value = min_value->get_value();
+ for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
+ if ((*c_iter)->get_value() < min_value_value) {
+ min_value = (*c_iter);
+ min_value_value = min_value->get_value();
+ }
+ }
+ }
+ return min_value;
+ }
+
+ t_enum_value* get_max_value() const {
+ const std::vector<t_enum_value*>& enum_values = get_constants();
+ std::vector<t_enum_value*>::const_iterator c_iter;
+ t_enum_value* max_value;
+ if (enum_values.size() == 0) {
+ max_value = nullptr;
+ } else {
+ int max_value_value;
+ max_value = enum_values.back();
+ max_value_value = max_value->get_value();
+ for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
+ if ((*c_iter)->get_value() > max_value_value) {
+ max_value = (*c_iter);
+ max_value_value = max_value->get_value();
+ }
+ }
+ }
+ return max_value;
+ }
+
+ bool is_enum() const override { return true; }
+
+private:
+ std::vector<t_enum_value*> constants_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h
new file mode 100644
index 000000000..70eee8618
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_ENUM_VALUE_H
+#define T_ENUM_VALUE_H
+
+#include <map>
+#include <string>
+#include "thrift/parse/t_doc.h"
+
+/**
+ * A constant. These are used inside of enum definitions. Constants are just
+ * symbol identifiers that may or may not have an explicit value associated
+ * with them.
+ *
+ */
+class t_enum_value : public t_doc {
+public:
+ t_enum_value(std::string name, int value) : name_(name), value_(value) {}
+
+ ~t_enum_value() override = default;
+
+ const std::string& get_name() const { return name_; }
+
+ int get_value() const { return value_; }
+
+ std::map<std::string, std::string> annotations_;
+
+private:
+ std::string name_;
+ int value_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_field.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_field.h
new file mode 100644
index 000000000..4be87706d
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_field.h
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_FIELD_H
+#define T_FIELD_H
+
+#include <map>
+#include <string>
+#include <sstream>
+
+#include "thrift/parse/t_doc.h"
+#include "thrift/parse/t_type.h"
+
+// Forward declare for xsd_attrs
+class t_struct;
+
+/**
+ * Class to represent a field in a thrift structure. A field has a data type,
+ * a symbolic name, and a numeric identifier.
+ *
+ */
+class t_field : public t_doc {
+public:
+ t_field(t_type* type, std::string name)
+ : type_(type),
+ name_(name),
+ key_(0),
+ value_(nullptr),
+ xsd_optional_(false),
+ xsd_nillable_(false),
+ xsd_attrs_(nullptr),
+ reference_(false) {}
+
+ t_field(t_type* type, std::string name, int32_t key)
+ : type_(type),
+ name_(name),
+ key_(key),
+ req_(T_OPT_IN_REQ_OUT),
+ value_(nullptr),
+ xsd_optional_(false),
+ xsd_nillable_(false),
+ xsd_attrs_(nullptr),
+ reference_(false) {}
+
+ ~t_field() override = default;
+
+ t_type* get_type() { return type_; }
+
+ const t_type* get_type() const { return type_; }
+
+ const std::string& get_name() const { return name_; }
+
+ int32_t get_key() const { return key_; }
+
+ enum e_req { T_REQUIRED, T_OPTIONAL, T_OPT_IN_REQ_OUT };
+
+ void set_req(e_req req) { req_ = req; }
+
+ e_req get_req() const { return req_; }
+
+ void set_value(t_const_value* value) { value_ = value; }
+
+ t_const_value* get_value() { return value_; }
+
+ const t_const_value* get_value() const { return value_; }
+
+ void set_xsd_optional(bool xsd_optional) { xsd_optional_ = xsd_optional; }
+
+ bool get_xsd_optional() const { return xsd_optional_; }
+
+ void set_xsd_nillable(bool xsd_nillable) { xsd_nillable_ = xsd_nillable; }
+
+ bool get_xsd_nillable() const { return xsd_nillable_; }
+
+ void set_xsd_attrs(t_struct* xsd_attrs) { xsd_attrs_ = xsd_attrs; }
+
+ t_struct* get_xsd_attrs() { return xsd_attrs_; }
+
+ const t_struct* get_xsd_attrs() const { return xsd_attrs_; }
+
+ /**
+ * Comparator to sort fields in ascending order by key.
+ * Make this a functor instead of a function to help GCC inline it.
+ * The arguments are (const) references to const pointers to const t_fields.
+ */
+ struct key_compare {
+ bool operator()(t_field const* const& a, t_field const* const& b) {
+ return a->get_key() < b->get_key();
+ }
+ };
+
+ std::map<std::string, std::string> annotations_;
+
+ bool get_reference() const { return reference_; }
+
+ void set_reference(bool reference) { reference_ = reference; }
+
+private:
+ t_type* type_;
+ std::string name_;
+ int32_t key_;
+ e_req req_;
+ t_const_value* value_;
+
+ bool xsd_optional_;
+ bool xsd_nillable_;
+ t_struct* xsd_attrs_;
+ bool reference_;
+};
+
+/**
+ * A simple struct for the parser to use to store a field ID, and whether or
+ * not it was specified by the user or automatically chosen.
+ */
+struct t_field_id {
+ int32_t value;
+ bool auto_assigned;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_function.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_function.h
new file mode 100644
index 000000000..d30c8a46e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_function.h
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_FUNCTION_H
+#define T_FUNCTION_H
+
+#include <string>
+#include "thrift/parse/t_type.h"
+#include "thrift/parse/t_struct.h"
+#include "thrift/parse/t_doc.h"
+
+/**
+ * Representation of a function. Key parts are return type, function name,
+ * optional modifiers, and an argument list, which is implemented as a thrift
+ * struct.
+ *
+ */
+class t_function : public t_doc {
+public:
+ t_function(t_type* returntype, std::string name, t_struct* arglist, bool oneway = false)
+ : returntype_(returntype),
+ name_(name),
+ arglist_(arglist),
+ xceptions_(new t_struct(nullptr)),
+ own_xceptions_(true),
+ oneway_(oneway) {
+ if (oneway_ && (!returntype_->is_void())) {
+ pwarning(1, "Oneway methods should return void.\n");
+ }
+ }
+
+ t_function(t_type* returntype,
+ std::string name,
+ t_struct* arglist,
+ t_struct* xceptions,
+ bool oneway = false)
+ : returntype_(returntype),
+ name_(name),
+ arglist_(arglist),
+ xceptions_(xceptions),
+ own_xceptions_(false),
+ oneway_(oneway) {
+ if (oneway_ && !xceptions_->get_members().empty()) {
+ throw std::string("Oneway methods can't throw exceptions.");
+ }
+ if (oneway_ && (!returntype_->is_void())) {
+ pwarning(1, "Oneway methods should return void.\n");
+ }
+ }
+
+ ~t_function() override {
+ if (own_xceptions_)
+ delete xceptions_;
+ }
+
+ t_type* get_returntype() const { return returntype_; }
+
+ const std::string& get_name() const { return name_; }
+
+ t_struct* get_arglist() const { return arglist_; }
+
+ t_struct* get_xceptions() const { return xceptions_; }
+
+ bool is_oneway() const { return oneway_; }
+
+ std::map<std::string, std::string> annotations_;
+
+private:
+ t_type* returntype_;
+ std::string name_;
+ t_struct* arglist_;
+ t_struct* xceptions_;
+ bool own_xceptions_;
+ bool oneway_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_list.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_list.h
new file mode 100644
index 000000000..f0b896d0c
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_list.h
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_LIST_H
+#define T_LIST_H
+
+#include "thrift/parse/t_container.h"
+
+/**
+ * A list is a lightweight container type that just wraps another data type.
+ *
+ */
+class t_list : public t_container {
+public:
+ t_list(t_type* elem_type) : elem_type_(elem_type) {}
+
+ t_type* get_elem_type() const { return elem_type_; }
+
+ bool is_list() const override { return true; }
+
+private:
+ t_type* elem_type_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_map.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_map.h
new file mode 100644
index 000000000..9614e6849
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_map.h
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_MAP_H
+#define T_MAP_H
+
+#include "thrift/parse/t_container.h"
+
+/**
+ * A map is a lightweight container type that just wraps another two data
+ * types.
+ *
+ */
+class t_map : public t_container {
+public:
+ t_map(t_type* key_type, t_type* val_type) : key_type_(key_type), val_type_(val_type) {}
+
+ t_type* get_key_type() const { return key_type_; }
+
+ t_type* get_val_type() const { return val_type_; }
+
+ bool is_map() const override { return true; }
+
+private:
+ t_type* key_type_;
+ t_type* val_type_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_program.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_program.h
new file mode 100644
index 000000000..e0b4b6ecd
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_program.h
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_PROGRAM_H
+#define T_PROGRAM_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+// For program_name()
+#include "thrift/main.h"
+
+#include "thrift/parse/t_doc.h"
+#include "thrift/parse/t_scope.h"
+#include "thrift/parse/t_base_type.h"
+#include "thrift/parse/t_typedef.h"
+#include "thrift/parse/t_enum.h"
+#include "thrift/parse/t_const.h"
+#include "thrift/parse/t_struct.h"
+#include "thrift/parse/t_service.h"
+#include "thrift/parse/t_list.h"
+#include "thrift/parse/t_map.h"
+#include "thrift/parse/t_set.h"
+#include "thrift/generate/t_generator_registry.h"
+//#include "thrift/parse/t_doc.h"
+
+/**
+ * Top level class representing an entire thrift program. A program consists
+ * fundamentally of the following:
+ *
+ * Typedefs
+ * Enumerations
+ * Constants
+ * Structs
+ * Exceptions
+ * Services
+ *
+ * The program module also contains the definitions of the base types.
+ *
+ */
+class t_program : public t_doc {
+public:
+ t_program(std::string path, std::string name)
+ : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope), recursive_(false) {}
+
+ t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false), recursive_(false) {
+ name_ = program_name(path);
+ scope_ = new t_scope();
+ }
+
+ ~t_program() override {
+ if (scope_) {
+ delete scope_;
+ scope_ = nullptr;
+ }
+ }
+
+ // Path accessor
+ const std::string& get_path() const { return path_; }
+
+ // Output path accessor
+ const std::string& get_out_path() const { return out_path_; }
+
+ // Create gen-* dir accessor
+ bool is_out_path_absolute() const { return out_path_is_absolute_; }
+
+ // Name accessor
+ const std::string& get_name() const { return name_; }
+
+ // Namespace
+ const std::string& get_namespace() const { return namespace_; }
+
+ // Include prefix accessor
+ const std::string& get_include_prefix() const { return include_prefix_; }
+
+ // Accessors for program elements
+ const std::vector<t_typedef*>& get_typedefs() const { return typedefs_; }
+ const std::vector<t_enum*>& get_enums() const { return enums_; }
+ const std::vector<t_const*>& get_consts() const { return consts_; }
+ const std::vector<t_struct*>& get_structs() const { return structs_; }
+ const std::vector<t_struct*>& get_xceptions() const { return xceptions_; }
+ const std::vector<t_struct*>& get_objects() const { return objects_; }
+ const std::vector<t_service*>& get_services() const { return services_; }
+ const std::map<std::string, std::string>& get_namespaces() const { return namespaces_; }
+
+ // Program elements
+ void add_typedef(t_typedef* td) { typedefs_.push_back(td); }
+ void add_enum(t_enum* te) { enums_.push_back(te); }
+ void add_const(t_const* tc) { consts_.push_back(tc); }
+ void add_struct(t_struct* ts) {
+ objects_.push_back(ts);
+ structs_.push_back(ts);
+ }
+ void add_xception(t_struct* tx) {
+ objects_.push_back(tx);
+ xceptions_.push_back(tx);
+ }
+ void add_service(t_service* ts) { services_.push_back(ts); }
+
+ // Programs to include
+ std::vector<t_program*>& get_includes() { return includes_; }
+
+ const std::vector<t_program*>& get_includes() const { return includes_; }
+
+ void set_out_path(std::string out_path, bool out_path_is_absolute) {
+ out_path_ = out_path;
+ out_path_is_absolute_ = out_path_is_absolute;
+ // Ensure that it ends with a trailing '/' (or '\' for windows machines)
+ char c = out_path_.at(out_path_.size() - 1);
+ if (!(c == '/' || c == '\\')) {
+ out_path_.push_back('/');
+ }
+ }
+
+ // Typename collision detection
+ /**
+ * Search for typename collisions
+ * @param t the type to test for collisions
+ * @return true if a certain collision was found, otherwise false
+ */
+ bool is_unique_typename(const t_type* t) const {
+ int occurrences = program_typename_count(this, t);
+ for (auto it = includes_.cbegin(); it != includes_.cend(); ++it) {
+ occurrences += program_typename_count(*it, t);
+ }
+ return 0 == occurrences;
+ }
+
+ /**
+ * Search all type collections for duplicate typenames
+ * @param prog the program to search
+ * @param t the type to test for collisions
+ * @return the number of certain typename collisions
+ */
+ int program_typename_count(const t_program* prog, const t_type* t) const {
+ int occurrences = 0;
+ occurrences += collection_typename_count(prog, prog->typedefs_, t);
+ occurrences += collection_typename_count(prog, prog->enums_, t);
+ occurrences += collection_typename_count(prog, prog->objects_, t);
+ occurrences += collection_typename_count(prog, prog->services_, t);
+ return occurrences;
+ }
+
+ /**
+ * Search a type collection for duplicate typenames
+ * @param prog the program to search
+ * @param type_collection the type collection to search
+ * @param t the type to test for collisions
+ * @return the number of certain typename collisions
+ */
+ template <class T>
+ int collection_typename_count(const t_program* prog, const T type_collection, const t_type* t) const {
+ int occurrences = 0;
+ for (auto it = type_collection.cbegin(); it != type_collection.cend(); ++it)
+ if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t))
+ ++occurrences;
+ return occurrences;
+ }
+
+ /**
+ * Determine whether identical typenames will collide based on namespaces.
+ *
+ * Because we do not know which languages the user will generate code for,
+ * collisions within programs (IDL files) having namespace declarations can be
+ * difficult to determine. Only guaranteed collisions return true (cause an error).
+ * Possible collisions involving explicit namespace declarations produce a warning.
+ * Other possible collisions go unreported.
+ * @param prog the program containing the preexisting typename
+ * @param t the type containing the typename match
+ * @return true if a collision within namespaces is found, otherwise false
+ */
+ bool is_common_namespace(const t_program* prog, const t_type* t) const {
+ // Case 1: Typenames are in the same program [collision]
+ if (prog == t->get_program()) {
+ pwarning(1,
+ "Duplicate typename %s found in %s",
+ t->get_name().c_str(),
+ t->get_program()->get_name().c_str());
+ return true;
+ }
+
+ // Case 2: Both programs have identical namespace scope/name declarations [collision]
+ bool match = true;
+ for (auto it = prog->namespaces_.cbegin();
+ it != prog->namespaces_.cend();
+ ++it) {
+ if (0 == it->second.compare(t->get_program()->get_namespace(it->first))) {
+ pwarning(1,
+ "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]",
+ t->get_name().c_str(),
+ t->get_program()->get_name().c_str(),
+ it->first.c_str(),
+ it->second.c_str(),
+ prog->get_name().c_str(),
+ it->first.c_str(),
+ it->second.c_str());
+ } else {
+ match = false;
+ }
+ }
+ for (auto it = t->get_program()->namespaces_.cbegin();
+ it != t->get_program()->namespaces_.cend();
+ ++it) {
+ if (0 == it->second.compare(prog->get_namespace(it->first))) {
+ pwarning(1,
+ "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]",
+ t->get_name().c_str(),
+ t->get_program()->get_name().c_str(),
+ it->first.c_str(),
+ it->second.c_str(),
+ prog->get_name().c_str(),
+ it->first.c_str(),
+ it->second.c_str());
+ } else {
+ match = false;
+ }
+ }
+ if (0 == prog->namespaces_.size() && 0 == t->get_program()->namespaces_.size()) {
+ pwarning(1,
+ "Duplicate typename %s found in %s and %s",
+ t->get_name().c_str(),
+ t->get_program()->get_name().c_str(),
+ prog->get_name().c_str());
+ }
+ return match;
+ }
+
+ // Scoping and namespacing
+ void set_namespace(std::string name) { namespace_ = name; }
+
+ // Scope accessor
+ t_scope* scope() { return scope_; }
+
+ const t_scope* scope() const { return scope_; }
+
+ // Includes
+
+ void add_include(t_program* program) {
+ includes_.push_back(program);
+ }
+
+ void add_include(std::string path, std::string include_site) {
+ t_program* program = new t_program(path);
+
+ // include prefix for this program is the site at which it was included
+ // (minus the filename)
+ std::string include_prefix;
+ std::string::size_type last_slash = std::string::npos;
+ if ((last_slash = include_site.rfind("/")) != std::string::npos) {
+ include_prefix = include_site.substr(0, last_slash);
+ }
+
+ program->set_include_prefix(include_prefix);
+ includes_.push_back(program);
+ }
+
+ void set_include_prefix(std::string include_prefix) {
+ include_prefix_ = include_prefix;
+
+ // this is intended to be a directory; add a trailing slash if necessary
+ std::string::size_type len = include_prefix_.size();
+ if (len > 0 && include_prefix_[len - 1] != '/') {
+ include_prefix_ += '/';
+ }
+ }
+
+ // Language neutral namespace / packaging
+ void set_namespace(std::string language, std::string name_space) {
+ if (language != "*") {
+ size_t sub_index = language.find('.');
+ std::string base_language = language.substr(0, sub_index);
+
+ if (base_language == "smalltalk") {
+ pwarning(1, "Namespace 'smalltalk' is deprecated. Use 'st' instead");
+ base_language = "st";
+ }
+ else if ((base_language == "csharp") || (base_language == "netcore")) {
+ pwarning(1, "The '%s' target is deprecated. Consider moving to 'netstd' instead.", base_language.c_str());
+ // warn only, don't change base_language
+ }
+
+ t_generator_registry::gen_map_t my_copy = t_generator_registry::get_generator_map();
+
+ t_generator_registry::gen_map_t::iterator it;
+ it = my_copy.find(base_language);
+
+ if (it == my_copy.end()) {
+ std::string warning = "No generator named '" + base_language + "' could be found!";
+ pwarning(1, warning.c_str());
+ } else {
+ if (sub_index != std::string::npos) {
+ std::string sub_namespace = language.substr(sub_index + 1);
+ if (!it->second->is_valid_namespace(sub_namespace)) {
+ std::string warning = base_language + " generator does not accept '" + sub_namespace
+ + "' as sub-namespace!";
+ pwarning(1, warning.c_str());
+ }
+ }
+ }
+ }
+
+ namespaces_[language] = name_space;
+ }
+
+ std::string get_namespace(std::string language) const {
+ std::map<std::string, std::string>::const_iterator iter;
+ if ((iter = namespaces_.find(language)) != namespaces_.end()
+ || (iter = namespaces_.find("*")) != namespaces_.end()) {
+ return iter->second;
+ }
+ return std::string();
+ }
+
+ const std::map<std::string, std::string>& get_all_namespaces() const {
+ return namespaces_;
+ }
+
+ void set_namespace_annotations(std::string language, std::map<std::string, std::string> annotations) {
+ namespace_annotations_[language] = annotations;
+ }
+
+ const std::map<std::string, std::string>& get_namespace_annotations(const std::string& language) const {
+ auto it = namespace_annotations_.find(language);
+ if (namespace_annotations_.end() != it) {
+ return it->second;
+ }
+ static const std::map<std::string, std::string> emptyMap;
+ return emptyMap;
+ }
+
+ std::map<std::string, std::string>& get_namespace_annotations(const std::string& language) {
+ return namespace_annotations_[language];
+ }
+
+ // Language specific namespace / packaging
+
+ void add_cpp_include(std::string path) { cpp_includes_.push_back(path); }
+
+ const std::vector<std::string>& get_cpp_includes() const { return cpp_includes_; }
+
+ void add_c_include(std::string path) { c_includes_.push_back(path); }
+
+ const std::vector<std::string>& get_c_includes() const { return c_includes_; }
+
+ void set_recursive(const bool recursive) { recursive_ = recursive; }
+
+ bool get_recursive() const { return recursive_; }
+
+private:
+ // File path
+ std::string path_;
+
+ // Name
+ std::string name_;
+
+ // Output directory
+ std::string out_path_;
+
+ // Output directory is absolute location for generated source (no gen-*)
+ bool out_path_is_absolute_;
+
+ // Namespace
+ std::string namespace_;
+
+ // Included programs
+ std::vector<t_program*> includes_;
+
+ // Include prefix for this program, if any
+ std::string include_prefix_;
+
+ // Identifier lookup scope
+ t_scope* scope_;
+
+ // Components to generate code for
+ std::vector<t_typedef*> typedefs_;
+ std::vector<t_enum*> enums_;
+ std::vector<t_const*> consts_;
+ std::vector<t_struct*> objects_;
+ std::vector<t_struct*> structs_;
+ std::vector<t_struct*> xceptions_;
+ std::vector<t_service*> services_;
+
+ // Dynamic namespaces
+ std::map<std::string, std::string> namespaces_;
+
+ // Annotations for dynamic namespaces
+ std::map<std::string, std::map<std::string, std::string> > namespace_annotations_;
+
+ // C++ extra includes
+ std::vector<std::string> cpp_includes_;
+
+ // C extra includes
+ std::vector<std::string> c_includes_;
+
+ // Recursive code generation
+ bool recursive_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_scope.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_scope.h
new file mode 100644
index 000000000..a12c4df5e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_scope.h
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_SCOPE_H
+#define T_SCOPE_H
+
+#include <map>
+#include <string>
+#include <sstream>
+
+#include "thrift/parse/t_type.h"
+#include "thrift/parse/t_service.h"
+#include "thrift/parse/t_const.h"
+#include "thrift/parse/t_const_value.h"
+#include "thrift/parse/t_base_type.h"
+#include "thrift/parse/t_map.h"
+#include "thrift/parse/t_list.h"
+#include "thrift/parse/t_set.h"
+
+/**
+ * This represents a variable scope used for looking up predefined types and
+ * services. Typically, a scope is associated with a t_program. Scopes are not
+ * used to determine code generation, but rather to resolve identifiers at
+ * parse time.
+ *
+ */
+class t_scope {
+public:
+ t_scope() = default;
+
+ void add_type(std::string name, t_type* type) { types_[name] = type; }
+
+ t_type* get_type(std::string name) { return types_[name]; }
+
+ const t_type* get_type(std::string name) const {
+ const auto it = types_.find(name);
+ if (types_.end() != it)
+ {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+ void add_service(std::string name, t_service* service) { services_[name] = service; }
+
+ t_service* get_service(std::string name) { return services_[name]; }
+
+ const t_service* get_service(std::string name) const {
+ const auto it = services_.find(name);
+ if (services_.end() != it)
+ {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+ void add_constant(std::string name, t_const* constant) {
+ if (constants_.find(name) != constants_.end()) {
+ throw "Enum " + name + " is already defined!";
+ } else {
+ constants_[name] = constant;
+ }
+ }
+
+ t_const* get_constant(std::string name) { return constants_[name]; }
+
+ const t_const* get_constant(std::string name) const {
+ const auto it = constants_.find(name);
+ if (constants_.end() != it)
+ {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+ void print() {
+ std::map<std::string, t_type*>::iterator iter;
+ for (iter = types_.begin(); iter != types_.end(); ++iter) {
+ printf("%s => %s\n", iter->first.c_str(), iter->second->get_name().c_str());
+ }
+ }
+
+ void resolve_const_value(t_const_value* const_val, t_type* ttype) {
+ if (ttype->is_map()) {
+ const std::map<t_const_value*, t_const_value*, t_const_value::value_compare>& map = const_val->get_map();
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = map.begin(); v_iter != map.end(); ++v_iter) {
+ resolve_const_value(v_iter->first, ((t_map*)ttype)->get_key_type());
+ resolve_const_value(v_iter->second, ((t_map*)ttype)->get_val_type());
+ }
+ } else if (ttype->is_list()) {
+ const std::vector<t_const_value*>& val = const_val->get_list();
+ std::vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ resolve_const_value((*v_iter), ((t_list*)ttype)->get_elem_type());
+ }
+ } else if (ttype->is_set()) {
+ const std::vector<t_const_value*>& val = const_val->get_list();
+ std::vector<t_const_value*>::const_iterator v_iter;
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ resolve_const_value((*v_iter), ((t_set*)ttype)->get_elem_type());
+ }
+ } else if (ttype->is_struct()) {
+ auto* tstruct = (t_struct*)ttype;
+ const std::map<t_const_value*, t_const_value*, t_const_value::value_compare>& map = const_val->get_map();
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+ for (v_iter = map.begin(); v_iter != map.end(); ++v_iter) {
+ t_field* field = tstruct->get_field_by_name(v_iter->first->get_string());
+ if (field == nullptr) {
+ throw "No field named \"" + v_iter->first->get_string()
+ + "\" was found in struct of type \"" + tstruct->get_name() + "\"";
+ }
+ resolve_const_value(v_iter->second, field->get_type());
+ }
+ } else if (const_val->get_type() == t_const_value::CV_IDENTIFIER) {
+ if (ttype->is_enum()) {
+ const_val->set_enum((t_enum*)ttype);
+ } else {
+ t_const* constant = get_constant(const_val->get_identifier());
+ if (constant == nullptr) {
+ throw "No enum value or constant found named \"" + const_val->get_identifier() + "\"!";
+ }
+
+ // Resolve typedefs to the underlying type
+ t_type* const_type = constant->get_type()->get_true_type();
+
+ if (const_type->is_base_type()) {
+ switch (((t_base_type*)const_type)->get_base()) {
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ const_val->set_integer(constant->get_value()->get_integer());
+ break;
+ case t_base_type::TYPE_STRING:
+ const_val->set_string(constant->get_value()->get_string());
+ break;
+ case t_base_type::TYPE_DOUBLE:
+ const_val->set_double(constant->get_value()->get_double());
+ break;
+ case t_base_type::TYPE_VOID:
+ throw "Constants cannot be of type VOID";
+ }
+ } else if (const_type->is_map()) {
+ const std::map<t_const_value*, t_const_value*, t_const_value::value_compare>& map = constant->get_value()->get_map();
+ std::map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
+
+ const_val->set_map();
+ for (v_iter = map.begin(); v_iter != map.end(); ++v_iter) {
+ const_val->add_map(v_iter->first, v_iter->second);
+ }
+ } else if (const_type->is_list()) {
+ const std::vector<t_const_value*>& val = constant->get_value()->get_list();
+ std::vector<t_const_value*>::const_iterator v_iter;
+
+ const_val->set_list();
+ for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
+ const_val->add_list(*v_iter);
+ }
+ }
+ }
+ } else if (ttype->is_enum()) {
+ // enum constant with non-identifier value. set the enum and find the
+ // value's name.
+ auto* tenum = (t_enum*)ttype;
+ t_enum_value* enum_value = tenum->get_constant_by_value(const_val->get_integer());
+ if (enum_value == nullptr) {
+ std::ostringstream valstm;
+ valstm << const_val->get_integer();
+ throw "Couldn't find a named value in enum " + tenum->get_name() + " for value "
+ + valstm.str();
+ }
+ const_val->set_identifier(tenum->get_name() + "." + enum_value->get_name());
+ const_val->set_enum(tenum);
+ }
+ }
+
+private:
+ // Map of names to types
+ std::map<std::string, t_type*> types_;
+
+ // Map of names to constants
+ std::map<std::string, t_const*> constants_;
+
+ // Map of names to services
+ std::map<std::string, t_service*> services_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_service.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_service.h
new file mode 100644
index 000000000..a43a515ca
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_service.h
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_SERVICE_H
+#define T_SERVICE_H
+
+#include "thrift/parse/t_function.h"
+#include <vector>
+
+class t_program;
+
+/**
+ * A service consists of a set of functions.
+ *
+ */
+class t_service : public t_type {
+public:
+ t_service(t_program* program) : t_type(program), extends_(nullptr) {}
+
+ bool is_service() const override { return true; }
+
+ void set_extends(t_service* extends) { extends_ = extends; }
+
+ void add_function(t_function* func) {
+ std::vector<t_function*>::const_iterator iter;
+ for (iter = functions_.begin(); iter != functions_.end(); ++iter) {
+ if (func->get_name() == (*iter)->get_name()) {
+ throw "Function " + func->get_name() + " is already defined";
+ }
+ }
+ functions_.push_back(func);
+ }
+
+ const std::vector<t_function*>& get_functions() const { return functions_; }
+
+ t_service* get_extends() { return extends_; }
+
+ const t_service* get_extends() const { return extends_; }
+
+private:
+ std::vector<t_function*> functions_;
+ t_service* extends_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_set.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_set.h
new file mode 100644
index 000000000..c0d4a35c1
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_set.h
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_SET_H
+#define T_SET_H
+
+#include "thrift/parse/t_container.h"
+
+/**
+ * A set is a lightweight container type that just wraps another data type.
+ *
+ */
+class t_set : public t_container {
+public:
+ t_set(t_type* elem_type) : elem_type_(elem_type) {}
+
+ const t_type* get_elem_type() const { return elem_type_; }
+
+ t_type* get_elem_type() { return elem_type_; }
+
+ bool is_set() const override { return true; }
+
+private:
+ t_type* elem_type_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_struct.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_struct.h
new file mode 100644
index 000000000..7e1e6caf0
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_struct.h
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_STRUCT_H
+#define T_STRUCT_H
+
+#include <algorithm>
+#include <vector>
+#include <utility>
+#include <string>
+
+#include "thrift/parse/t_type.h"
+#include "thrift/parse/t_field.h"
+
+// Forward declare that puppy
+class t_program;
+
+/**
+ * A struct is a container for a set of member fields that has a name. Structs
+ * are also used to implement exception types.
+ *
+ */
+class t_struct : public t_type {
+public:
+ typedef std::vector<t_field*> members_type;
+
+ t_struct(t_program* program)
+ : t_type(program),
+ is_xception_(false),
+ is_union_(false),
+ members_validated(false),
+ members_with_value(0),
+ xsd_all_(false) {}
+
+ t_struct(t_program* program, const std::string& name)
+ : t_type(program, name),
+ is_xception_(false),
+ is_union_(false),
+ members_validated(false),
+ members_with_value(0),
+ xsd_all_(false) {}
+
+ void set_name(const std::string& name) override {
+ name_ = name;
+ validate_union_members();
+ }
+
+ void set_xception(bool is_xception) { is_xception_ = is_xception; }
+
+ void validate_union_member(t_field* field) {
+ if (is_union_ && (!name_.empty())) {
+
+ // 1) unions can't have required fields
+ // 2) union members are implicitly optional, otherwise bugs like THRIFT-3650 wait to happen
+ if (field->get_req() != t_field::T_OPTIONAL) {
+ // no warning on default requiredness, but do warn on anything else that is explicitly asked for
+ if(field->get_req() != t_field::T_OPT_IN_REQ_OUT) {
+ pwarning(1,
+ "Union %s field %s: union members must be optional, ignoring specified requiredness.\n",
+ name_.c_str(),
+ field->get_name().c_str());
+ }
+ field->set_req(t_field::T_OPTIONAL);
+ }
+
+ // unions may have up to one member defaulted, but not more
+ if (field->get_value() != nullptr) {
+ if (1 < ++members_with_value) {
+ throw "Error: Field " + field->get_name() + " provides another default value for union "
+ + name_;
+ }
+ }
+ }
+ }
+
+ void validate_union_members() {
+ if (is_union_ && (!name_.empty()) && (!members_validated)) {
+ members_type::const_iterator m_iter;
+ for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) {
+ validate_union_member(*m_iter);
+ }
+ members_validated = true;
+ }
+ }
+
+ void set_union(bool is_union) {
+ is_union_ = is_union;
+ validate_union_members();
+ }
+
+ void set_xsd_all(bool xsd_all) { xsd_all_ = xsd_all; }
+
+ bool get_xsd_all() const { return xsd_all_; }
+
+ bool append(t_field* elem) {
+ typedef members_type::iterator iter_type;
+ std::pair<iter_type, iter_type> bounds = std::equal_range(members_in_id_order_.begin(),
+ members_in_id_order_.end(),
+ elem,
+ t_field::key_compare());
+ if (bounds.first != bounds.second) {
+ return false;
+ }
+ // returns false when there is a conflict of field names
+ if (get_field_by_name(elem->get_name()) != nullptr) {
+ return false;
+ }
+ members_.push_back(elem);
+ members_in_id_order_.insert(bounds.second, elem);
+ validate_union_member(elem);
+ return true;
+ }
+
+ const members_type& get_members() const { return members_; }
+
+ const members_type& get_sorted_members() const { return members_in_id_order_; }
+
+ bool is_struct() const override { return !is_xception_; }
+
+ bool is_xception() const override { return is_xception_; }
+
+ bool is_union() const { return is_union_; }
+
+ t_field* get_field_by_name(std::string field_name) {
+ return const_cast<t_field*>(const_cast<const t_struct&>(*this).get_field_by_name(field_name));
+ }
+
+ const t_field* get_field_by_name(std::string field_name) const {
+ members_type::const_iterator m_iter;
+ for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) {
+ if ((*m_iter)->get_name() == field_name) {
+ return *m_iter;
+ }
+ }
+ return nullptr;
+ }
+
+private:
+ members_type members_;
+ members_type members_in_id_order_;
+ bool is_xception_;
+ bool is_union_;
+ bool members_validated;
+ int members_with_value;
+
+ bool xsd_all_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_type.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_type.h
new file mode 100644
index 000000000..63f99ed87
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_type.h
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_TYPE_H
+#define T_TYPE_H
+
+#include <string>
+#include <map>
+#include <cstring>
+#include <stdint.h>
+#include "thrift/parse/t_doc.h"
+
+class t_program;
+
+/**
+ * Generic representation of a thrift type. These objects are used by the
+ * parser module to build up a tree of object that are all explicitly typed.
+ * The generic t_type class exports a variety of useful methods that are
+ * used by the code generator to branch based upon different handling for the
+ * various types.
+ *
+ */
+class t_type : public t_doc {
+public:
+ ~t_type() override = default;
+
+ virtual void set_name(const std::string& name) { name_ = name; }
+
+ virtual const std::string& get_name() const { return name_; }
+
+ virtual bool is_void() const { return false; }
+ virtual bool is_base_type() const { return false; }
+ virtual bool is_string() const { return false; }
+ virtual bool is_binary() const { return false; }
+ virtual bool is_bool() const { return false; }
+ virtual bool is_typedef() const { return false; }
+ virtual bool is_enum() const { return false; }
+ virtual bool is_struct() const { return false; }
+ virtual bool is_xception() const { return false; }
+ virtual bool is_container() const { return false; }
+ virtual bool is_list() const { return false; }
+ virtual bool is_set() const { return false; }
+ virtual bool is_map() const { return false; }
+ virtual bool is_service() const { return false; }
+
+ t_program* get_program() { return program_; }
+
+ const t_program* get_program() const { return program_; }
+
+ t_type* get_true_type();
+ const t_type* get_true_type() const;
+
+ // This function will break (maybe badly) unless 0 <= num <= 16.
+ static char nybble_to_xdigit(int num) {
+ if (num < 10) {
+ return '0' + num;
+ } else {
+ return 'A' + num - 10;
+ }
+ }
+
+ static std::string byte_to_hex(uint8_t byte) {
+ std::string rv;
+ rv += nybble_to_xdigit(byte >> 4);
+ rv += nybble_to_xdigit(byte & 0x0f);
+ return rv;
+ }
+
+ std::map<std::string, std::string> annotations_;
+
+protected:
+ t_type() : program_(nullptr) { ; }
+
+ t_type(t_program* program) : program_(program) { ; }
+
+ t_type(t_program* program, std::string name) : program_(program), name_(name) { ; }
+
+ t_type(std::string name) : program_(nullptr), name_(name) { ; }
+
+ t_program* program_;
+ std::string name_;
+};
+
+/**
+ * Placeholder struct for returning the key and value of an annotation
+ * during parsing.
+ */
+struct t_annotation {
+ std::string key;
+ std::string val;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc
new file mode 100644
index 000000000..c80868560
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+#include <cstdio>
+
+#include "thrift/parse/t_typedef.h"
+#include "thrift/parse/t_program.h"
+
+t_type* t_typedef::get_type() {
+ return const_cast<t_type*>(const_cast<const t_typedef*>(this)->get_type());
+}
+
+const t_type* t_typedef::get_type() const {
+ if (type_ == NULL) {
+ const t_type* type = get_program()->scope()->get_type(symbolic_);
+ if (type == NULL) {
+ printf("Type \"%s\" not defined\n", symbolic_.c_str());
+ exit(1);
+ }
+ return type;
+ }
+ return type_;
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.h
new file mode 100644
index 000000000..d21d863a9
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/parse/t_typedef.h
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef T_TYPEDEF_H
+#define T_TYPEDEF_H
+
+#include <string>
+#include "thrift/parse/t_type.h"
+
+/**
+ * A typedef is a mapping from a symbolic name to another type. In dymanically
+ * typed languages (i.e. php/python) the code generator can actually usually
+ * ignore typedefs and just use the underlying type directly, though in C++
+ * the symbolic naming can be quite useful for code clarity.
+ *
+ */
+class t_typedef : public t_type {
+public:
+ t_typedef(t_program* program, t_type* type, const std::string& symbolic)
+ : t_type(program, symbolic), type_(type), symbolic_(symbolic), forward_(false) {}
+
+ /**
+ * This constructor is used to refer to a type that is lazily
+ * resolved at a later time, like for forward declarations or
+ * recursive types.
+ */
+ t_typedef(t_program* program, const std::string& symbolic, bool forward)
+ : t_type(program, symbolic),
+ type_(nullptr),
+ symbolic_(symbolic),
+ forward_(forward)
+ {}
+
+ ~t_typedef() override = default;
+
+ t_type* get_type();
+
+ const t_type* get_type() const;
+
+ const std::string& get_symbolic() const { return symbolic_; }
+
+ bool is_forward_typedef() const { return forward_; }
+
+ bool is_typedef() const override { return true; }
+
+private:
+ t_type* type_;
+ std::string symbolic_;
+ bool forward_;
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/platform.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/platform.h
new file mode 100644
index 000000000..f49adb9c7
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/platform.h
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * define for mkdir,since the method signature
+ * is different for the non-POSIX MinGW
+ */
+
+#ifdef _MSC_VER
+#include "thrift/windows/config.h"
+#endif
+
+#ifdef _WIN32
+#include <direct.h>
+#include <io.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#include <cerrno>
+
+// ignore EEXIST, throw on any other error
+#ifdef _WIN32
+#define MKDIR(x) { int r = _mkdir(x); if (r == -1 && errno != EEXIST) { throw (std::string(x) + ": ") + strerror(errno); } }
+#else
+#define MKDIR(x) { int r = mkdir(x, S_IRWXU | S_IRWXG | S_IRWXO); if (r == -1 && errno != EEXIST) { throw (std::string(x) + ": ") + strerror(errno); } }
+#endif
+
+#ifdef PATH_MAX
+#define THRIFT_PATH_MAX PATH_MAX
+#else
+#define THRIFT_PATH_MAX MAX_PATH
+#endif
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/thriftl.ll b/src/jaegertracing/thrift/compiler/cpp/src/thrift/thriftl.ll
new file mode 100644
index 000000000..282e0309c
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/thriftl.ll
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * Thrift scanner.
+ *
+ * Tokenizes a thrift definition file.
+ */
+
+%{
+
+/* This is redundant with some of the flags in Makefile.am, but it works
+ * when people override CXXFLAGS without being careful. The pragmas are
+ * the 'right' way to do it, but don't work on old-enough GCC (in particular
+ * the GCC that ship on Mac OS X 10.6.5, *counter* to what the GNU docs say)
+ *
+ * We should revert the Makefile.am changes once Apple ships a reasonable
+ * GCC.
+ */
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wunused-label"
+#endif
+
+#ifdef _MSC_VER
+#pragma warning( push )
+
+// warning C4102: 'find_rule' : unreferenced label
+#pragma warning( disable : 4102 )
+
+// warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
+#pragma warning( disable : 4267 )
+
+// avoid isatty redefinition
+#define YY_NEVER_INTERACTIVE 1
+
+#define YY_NO_UNISTD_H 1
+#endif
+
+#include <cassert>
+#include <string>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+#include "thrift/windows/config.h"
+#endif
+#include "thrift/main.h"
+#include "thrift/common.h"
+#include "thrift/globals.h"
+#include "thrift/parse/t_program.h"
+
+/**
+ * Must be included AFTER parse/t_program.h, but I can't remember why anymore
+ * because I wrote this a while ago.
+ */
+#if defined(BISON_USE_PARSER_H_EXTENSION)
+#include "thrift/thrifty.h"
+#else
+#include "thrift/thrifty.hh"
+#endif
+
+void integer_overflow(char* text) {
+ yyerror("This integer is too big: \"%s\"\n", text);
+ exit(1);
+}
+
+void unexpected_token(char* text) {
+ yyerror("Unexpected token in input: \"%s\"\n", text);
+ exit(1);
+}
+
+%}
+
+/**
+ * Provides the yylineno global, useful for debugging output
+ */
+%option lex-compat
+
+/**
+ * Our inputs are all single files, so no need for yywrap
+ */
+%option noyywrap
+
+/**
+ * We don't use it, and it fires up warnings at -Wall
+ */
+%option nounput
+
+/**
+ * Helper definitions, comments, constants, and whatnot
+ */
+
+intconstant ([+-]?[0-9]+)
+hexconstant ([+-]?"0x"[0-9A-Fa-f]+)
+dubconstant ([+-]?[0-9]*(\.[0-9]+)?([eE][+-]?[0-9]+)?)
+identifier ([a-zA-Z_](\.[a-zA-Z_0-9]|[a-zA-Z_0-9])*)
+whitespace ([ \t\r\n]*)
+sillycomm ("/*""*"*"*/")
+multicm_begin ("/*")
+doctext_begin ("/**")
+comment ("//"[^\n]*)
+unixcomment ("#"[^\n]*)
+symbol ([:;\,\{\}\(\)\=<>\[\]])
+literal_begin (['\"])
+
+%%
+
+{whitespace} { /* do nothing */ }
+{sillycomm} { /* do nothing */ }
+
+{doctext_begin} {
+ std::string parsed("/**");
+ int state = 0; // 0 = normal, 1 = "*" seen, "*/" seen
+ while(state < 2)
+ {
+ int ch = yyinput();
+ parsed.push_back(ch);
+ switch (ch) {
+ case EOF:
+ yyerror("Unexpected end of file in doc-comment at %d\n", yylineno);
+ exit(1);
+ case '*':
+ state = 1;
+ break;
+ case '/':
+ state = (state == 1) ? 2 : 0;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ }
+ pdebug("doctext = \"%s\"\n",parsed.c_str());
+
+ /* This does not show up in the parse tree. */
+ /* Rather, the parser will grab it out of the global. */
+ if (g_parse_mode == PROGRAM) {
+ clear_doctext();
+ g_doctext = strdup(parsed.c_str() + 3);
+ assert(strlen(g_doctext) >= 2);
+ g_doctext[strlen(g_doctext) - 2] = ' ';
+ g_doctext[strlen(g_doctext) - 1] = '\0';
+ g_doctext = clean_up_doctext(g_doctext);
+ g_doctext_lineno = yylineno;
+ if( (g_program_doctext_candidate == NULL) && (g_program_doctext_status == INVALID)){
+ g_program_doctext_candidate = strdup(g_doctext);
+ g_program_doctext_lineno = g_doctext_lineno;
+ g_program_doctext_status = STILL_CANDIDATE;
+ pdebug("%s","program doctext set to STILL_CANDIDATE");
+ }
+ }
+}
+
+{multicm_begin} { /* parsed, but thrown away */
+ std::string parsed("/*");
+ int state = 0; // 0 = normal, 1 = "*" seen, "*/" seen
+ while(state < 2)
+ {
+ int ch = yyinput();
+ parsed.push_back(ch);
+ switch (ch) {
+ case EOF:
+ yyerror("Unexpected end of file in multiline comment at %d\n", yylineno);
+ exit(1);
+ case '*':
+ state = 1;
+ break;
+ case '/':
+ state = (state == 1) ? 2 : 0;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ }
+ pdebug("multi_comm = \"%s\"\n",parsed.c_str());
+}
+
+{comment} { /* do nothing */ }
+{unixcomment} { /* do nothing */ }
+
+{symbol} { return yytext[0]; }
+"*" { return yytext[0]; }
+
+"false" { yylval.iconst=0; return tok_int_constant; }
+"true" { yylval.iconst=1; return tok_int_constant; }
+
+"namespace" { return tok_namespace; }
+"cpp_namespace" { error_unsupported_namespace_decl("cpp"); /* do nothing */ }
+"cpp_include" { return tok_cpp_include; }
+"cpp_type" { return tok_cpp_type; }
+"java_package" { error_unsupported_namespace_decl("java_package", "java"); /* do nothing */ }
+"csharp_namespace" { error_unsupported_namespace_decl("csharp"); /* do nothing */ }
+"delphi_namespace" { error_unsupported_namespace_decl("delphi"); /* do nothing */ }
+"php_namespace" { error_unsupported_namespace_decl("php"); /* do nothing */ }
+"py_module" { error_unsupported_namespace_decl("py_module", "py"); /* do nothing */ }
+"perl_package" { error_unsupported_namespace_decl("perl_package", "perl"); /* do nothing */ }
+"ruby_namespace" { error_unsupported_namespace_decl("ruby"); /* do nothing */ }
+"smalltalk_category" { error_unsupported_namespace_decl("smalltalk_category", "st"); /* do nothing */ }
+"smalltalk_prefix" { error_unsupported_namespace_decl("smalltalk_prefix", "st"); /* do nothing */ }
+"xsd_all" { return tok_xsd_all; }
+"xsd_optional" { return tok_xsd_optional; }
+"xsd_nillable" { return tok_xsd_nillable; }
+"xsd_namespace" { error_unsupported_namespace_decl("xsd"); /* do nothing */ }
+"xsd_attrs" { return tok_xsd_attrs; }
+"include" { return tok_include; }
+"void" { return tok_void; }
+"bool" { return tok_bool; }
+"byte" {
+ emit_byte_type_warning();
+ return tok_i8;
+}
+"i8" { return tok_i8; }
+"i16" { return tok_i16; }
+"i32" { return tok_i32; }
+"i64" { return tok_i64; }
+"double" { return tok_double; }
+"string" { return tok_string; }
+"binary" { return tok_binary; }
+"slist" {
+ pwarning(0, "\"slist\" is deprecated and will be removed in a future compiler version. This type should be replaced with \"string\".\n");
+ return tok_slist;
+}
+"senum" {
+ pwarning(0, "\"senum\" is deprecated and will be removed in a future compiler version. This type should be replaced with \"string\".\n");
+ return tok_senum;
+}
+"map" { return tok_map; }
+"list" { return tok_list; }
+"set" { return tok_set; }
+"oneway" { return tok_oneway; }
+"typedef" { return tok_typedef; }
+"struct" { return tok_struct; }
+"union" { return tok_union; }
+"exception" { return tok_xception; }
+"extends" { return tok_extends; }
+"throws" { return tok_throws; }
+"service" { return tok_service; }
+"enum" { return tok_enum; }
+"const" { return tok_const; }
+"required" { return tok_required; }
+"optional" { return tok_optional; }
+"async" {
+ pwarning(0, "\"async\" is deprecated. It is called \"oneway\" now.\n");
+ return tok_oneway;
+}
+"&" { return tok_reference; }
+
+{intconstant} {
+ errno = 0;
+ yylval.iconst = strtoll(yytext, NULL, 10);
+ if (errno == ERANGE) {
+ integer_overflow(yytext);
+ }
+ return tok_int_constant;
+}
+
+{hexconstant} {
+ errno = 0;
+ char sign = yytext[0];
+ int shift = sign == '0' ? 2 : 3;
+ yylval.iconst = strtoll(yytext+shift, NULL, 16);
+ if (sign == '-') {
+ yylval.iconst = -yylval.iconst;
+ }
+ if (errno == ERANGE) {
+ integer_overflow(yytext);
+ }
+ return tok_int_constant;
+}
+
+{identifier} {
+ yylval.id = strdup(yytext);
+ return tok_identifier;
+}
+
+{dubconstant} {
+ /* Deliberately placed after identifier, since "e10" is NOT a double literal (THRIFT-3477) */
+ yylval.dconst = atof(yytext);
+ return tok_dub_constant;
+}
+
+{literal_begin} {
+ char mark = yytext[0];
+ std::string result;
+ for(;;)
+ {
+ int ch = yyinput();
+ switch (ch) {
+ case EOF:
+ yyerror("End of file while read string at %d\n", yylineno);
+ exit(1);
+ case '\n':
+ yyerror("End of line while read string at %d\n", yylineno - 1);
+ exit(1);
+ case '\\':
+ ch = yyinput();
+ switch (ch) {
+ case 'r':
+ result.push_back('\r');
+ continue;
+ case 'n':
+ result.push_back('\n');
+ continue;
+ case 't':
+ result.push_back('\t');
+ continue;
+ case '"':
+ result.push_back('"');
+ continue;
+ case '\'':
+ result.push_back('\'');
+ continue;
+ case '\\':
+ result.push_back('\\');
+ continue;
+ default:
+ yyerror("Bad escape character\n");
+ return -1;
+ }
+ break;
+ default:
+ if (ch == mark) {
+ yylval.id = strdup(result.c_str());
+ return tok_literal;
+ } else {
+ result.push_back(ch);
+ }
+ }
+ }
+}
+
+
+. {
+ unexpected_token(yytext);
+}
+
+%%
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+/* vim: filetype=lex
+*/
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/thrifty.yy b/src/jaegertracing/thrift/compiler/cpp/src/thrift/thrifty.yy
new file mode 100644
index 000000000..df34adf04
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/thrifty.yy
@@ -0,0 +1,1201 @@
+%code requires {
+#include "thrift/parse/t_program.h"
+}
+%{
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+/**
+ * Thrift parser.
+ *
+ * This parser is used on a thrift definition file.
+ *
+ */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <stdio.h>
+#include <string.h>
+#ifndef _MSC_VER
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+#include <limits.h>
+#ifdef _MSC_VER
+#include "thrift/windows/config.h"
+#endif
+#include "thrift/main.h"
+#include "thrift/common.h"
+#include "thrift/globals.h"
+#include "thrift/parse/t_program.h"
+#include "thrift/parse/t_scope.h"
+
+#ifdef _MSC_VER
+//warning C4065: switch statement contains 'default' but no 'case' labels
+#pragma warning(disable:4065)
+#endif
+
+/**
+ * This global variable is used for automatic numbering of field indices etc.
+ * when parsing the members of a struct. Field values are automatically
+ * assigned starting from -1 and working their way down.
+ */
+int y_field_val = -1;
+/**
+ * This global variable is used for automatic numbering of enum values.
+ * y_enum_val is the last value assigned; the next auto-assigned value will be
+ * y_enum_val+1, and then it continues working upwards. Explicitly specified
+ * enum values reset y_enum_val to that value.
+ */
+int32_t y_enum_val = -1;
+int g_arglist = 0;
+const int struct_is_struct = 0;
+const int struct_is_union = 1;
+
+%}
+
+/**
+ * This structure is used by the parser to hold the data types associated with
+ * various parse nodes.
+ */
+%union {
+ char* id;
+ int64_t iconst;
+ double dconst;
+ bool tbool;
+ t_doc* tdoc;
+ t_type* ttype;
+ t_base_type* tbase;
+ t_typedef* ttypedef;
+ t_enum* tenum;
+ t_enum_value* tenumv;
+ t_const* tconst;
+ t_const_value* tconstv;
+ t_struct* tstruct;
+ t_service* tservice;
+ t_function* tfunction;
+ t_field* tfield;
+ char* dtext;
+ t_field::e_req ereq;
+ t_annotation* tannot;
+ t_field_id tfieldid;
+}
+
+/**
+ * Strings identifier
+ */
+%token<id> tok_identifier
+%token<id> tok_literal
+%token<dtext> tok_doctext
+
+/**
+ * Constant values
+ */
+%token<iconst> tok_int_constant
+%token<dconst> tok_dub_constant
+
+/**
+ * Header keywords
+ */
+%token tok_include
+%token tok_namespace
+%token tok_cpp_include
+%token tok_cpp_type
+%token tok_xsd_all
+%token tok_xsd_optional
+%token tok_xsd_nillable
+%token tok_xsd_attrs
+
+/**
+ * Base datatype keywords
+ */
+%token tok_void
+%token tok_bool
+%token tok_string
+%token tok_binary
+%token tok_slist
+%token tok_senum
+%token tok_i8
+%token tok_i16
+%token tok_i32
+%token tok_i64
+%token tok_double
+
+/**
+ * Complex type keywords
+ */
+%token tok_map
+%token tok_list
+%token tok_set
+
+/**
+ * Function modifiers
+ */
+%token tok_oneway
+
+/**
+ * Thrift language keywords
+ */
+%token tok_typedef
+%token tok_struct
+%token tok_xception
+%token tok_throws
+%token tok_extends
+%token tok_service
+%token tok_enum
+%token tok_const
+%token tok_required
+%token tok_optional
+%token tok_union
+%token tok_reference
+
+/**
+ * Grammar nodes
+ */
+
+%type<ttype> BaseType
+%type<ttype> SimpleBaseType
+%type<ttype> ContainerType
+%type<ttype> SimpleContainerType
+%type<ttype> MapType
+%type<ttype> SetType
+%type<ttype> ListType
+
+%type<tdoc> Definition
+%type<ttype> TypeDefinition
+
+%type<ttypedef> Typedef
+
+%type<ttype> TypeAnnotations
+%type<ttype> TypeAnnotationList
+%type<tannot> TypeAnnotation
+%type<id> TypeAnnotationValue
+
+%type<tfield> Field
+%type<tfieldid> FieldIdentifier
+%type<ereq> FieldRequiredness
+%type<ttype> FieldType
+%type<tconstv> FieldValue
+%type<tstruct> FieldList
+%type<tbool> FieldReference
+
+%type<tenum> Enum
+%type<tenum> EnumDefList
+%type<tenumv> EnumDef
+%type<tenumv> EnumValue
+
+%type<ttypedef> Senum
+%type<tbase> SenumDefList
+%type<id> SenumDef
+
+%type<tconst> Const
+%type<tconstv> ConstValue
+%type<tconstv> ConstList
+%type<tconstv> ConstListContents
+%type<tconstv> ConstMap
+%type<tconstv> ConstMapContents
+
+%type<iconst> StructHead
+%type<tstruct> Struct
+%type<tstruct> Xception
+%type<tservice> Service
+
+%type<tfunction> Function
+%type<ttype> FunctionType
+%type<tservice> FunctionList
+
+%type<tstruct> Throws
+%type<tservice> Extends
+%type<tbool> Oneway
+%type<tbool> XsdAll
+%type<tbool> XsdOptional
+%type<tbool> XsdNillable
+%type<tstruct> XsdAttributes
+%type<id> CppType
+
+%type<dtext> CaptureDocText
+
+%%
+
+/**
+ * Thrift Grammar Implementation.
+ *
+ * For the most part this source file works its way top down from what you
+ * might expect to find in a typical .thrift file, i.e. type definitions and
+ * namespaces up top followed by service definitions using those types.
+ */
+
+Program:
+ HeaderList DefinitionList
+ {
+ pdebug("Program -> Headers DefinitionList");
+ if((g_program_doctext_candidate != NULL) && (g_program_doctext_status != ALREADY_PROCESSED))
+ {
+ g_program->set_doc(g_program_doctext_candidate);
+ g_program_doctext_status = ALREADY_PROCESSED;
+ }
+ clear_doctext();
+ }
+
+CaptureDocText:
+ {
+ if (g_parse_mode == PROGRAM) {
+ $$ = g_doctext;
+ g_doctext = NULL;
+ } else {
+ $$ = NULL;
+ }
+ }
+
+/* TODO(dreiss): Try to DestroyDocText in all sorts or random places. */
+DestroyDocText:
+ {
+ if (g_parse_mode == PROGRAM) {
+ clear_doctext();
+ }
+ }
+
+/* We have to DestroyDocText here, otherwise it catches the doctext
+ on the first real element. */
+HeaderList:
+ HeaderList DestroyDocText Header
+ {
+ pdebug("HeaderList -> HeaderList Header");
+ }
+|
+ {
+ pdebug("HeaderList -> ");
+ }
+
+Header:
+ Include
+ {
+ pdebug("Header -> Include");
+ }
+| tok_namespace tok_identifier tok_identifier TypeAnnotations
+ {
+ pdebug("Header -> tok_namespace tok_identifier tok_identifier");
+ declare_valid_program_doctext();
+ if (g_parse_mode == PROGRAM) {
+ g_program->set_namespace($2, $3);
+ }
+ if ($4 != NULL) {
+ g_program->set_namespace_annotations($2, $4->annotations_);
+ delete $4;
+ }
+ }
+| tok_namespace '*' tok_identifier
+ {
+ pdebug("Header -> tok_namespace * tok_identifier");
+ declare_valid_program_doctext();
+ if (g_parse_mode == PROGRAM) {
+ g_program->set_namespace("*", $3);
+ }
+ }
+| tok_cpp_include tok_literal
+ {
+ pdebug("Header -> tok_cpp_include tok_literal");
+ declare_valid_program_doctext();
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_cpp_include($2);
+ }
+ }
+
+Include:
+ tok_include tok_literal
+ {
+ pdebug("Include -> tok_include tok_literal");
+ declare_valid_program_doctext();
+ if (g_parse_mode == INCLUDES) {
+ std::string path = include_file(std::string($2));
+ if (!path.empty()) {
+ g_program->add_include(path, std::string($2));
+ }
+ }
+ }
+
+DefinitionList:
+ DefinitionList CaptureDocText Definition
+ {
+ pdebug("DefinitionList -> DefinitionList Definition");
+ if ($2 != NULL && $3 != NULL) {
+ $3->set_doc($2);
+ }
+ }
+|
+ {
+ pdebug("DefinitionList -> ");
+ }
+
+Definition:
+ Const
+ {
+ pdebug("Definition -> Const");
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_const($1);
+ }
+ $$ = $1;
+ }
+| TypeDefinition
+ {
+ pdebug("Definition -> TypeDefinition");
+ if (g_parse_mode == PROGRAM) {
+ g_scope->add_type($1->get_name(), $1);
+ if (g_parent_scope != NULL) {
+ g_parent_scope->add_type(g_parent_prefix + $1->get_name(), $1);
+ }
+ if (! g_program->is_unique_typename($1)) {
+ yyerror("Type \"%s\" is already defined.", $1->get_name().c_str());
+ exit(1);
+ }
+ }
+ $$ = $1;
+ }
+| Service
+ {
+ pdebug("Definition -> Service");
+ if (g_parse_mode == PROGRAM) {
+ g_scope->add_service($1->get_name(), $1);
+ if (g_parent_scope != NULL) {
+ g_parent_scope->add_service(g_parent_prefix + $1->get_name(), $1);
+ }
+ g_program->add_service($1);
+ if (! g_program->is_unique_typename($1)) {
+ yyerror("Type \"%s\" is already defined.", $1->get_name().c_str());
+ exit(1);
+ }
+ }
+ $$ = $1;
+ }
+
+TypeDefinition:
+ Typedef
+ {
+ pdebug("TypeDefinition -> Typedef");
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_typedef($1);
+ }
+ }
+| Enum
+ {
+ pdebug("TypeDefinition -> Enum");
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_enum($1);
+ }
+ }
+| Senum
+ {
+ pdebug("TypeDefinition -> Senum");
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_typedef($1);
+ }
+ }
+| Struct
+ {
+ pdebug("TypeDefinition -> Struct");
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_struct($1);
+ }
+ }
+| Xception
+ {
+ pdebug("TypeDefinition -> Xception");
+ if (g_parse_mode == PROGRAM) {
+ g_program->add_xception($1);
+ }
+ }
+
+CommaOrSemicolonOptional:
+ ','
+ {}
+| ';'
+ {}
+|
+ {}
+
+Typedef:
+ tok_typedef FieldType tok_identifier TypeAnnotations CommaOrSemicolonOptional
+ {
+ pdebug("TypeDef -> tok_typedef FieldType tok_identifier");
+ validate_simple_identifier( $3);
+ t_typedef *td = new t_typedef(g_program, $2, $3);
+ $$ = td;
+ if ($4 != NULL) {
+ $$->annotations_ = $4->annotations_;
+ delete $4;
+ }
+ }
+
+Enum:
+ tok_enum tok_identifier '{' EnumDefList '}' TypeAnnotations
+ {
+ pdebug("Enum -> tok_enum tok_identifier { EnumDefList }");
+ $$ = $4;
+ validate_simple_identifier( $2);
+ $$->set_name($2);
+ if ($6 != NULL) {
+ $$->annotations_ = $6->annotations_;
+ delete $6;
+ }
+
+ // make constants for all the enum values
+ if (g_parse_mode == PROGRAM) {
+ const std::vector<t_enum_value*>& enum_values = $$->get_constants();
+ std::vector<t_enum_value*>::const_iterator c_iter;
+ for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) {
+ std::string const_name = $$->get_name() + "." + (*c_iter)->get_name();
+ t_const_value* const_val = new t_const_value((*c_iter)->get_value());
+ const_val->set_enum($$);
+ g_scope->add_constant(const_name, new t_const(g_type_i32, (*c_iter)->get_name(), const_val));
+ if (g_parent_scope != NULL) {
+ g_parent_scope->add_constant(g_parent_prefix + const_name, new t_const(g_type_i32, (*c_iter)->get_name(), const_val));
+ }
+ }
+ }
+ }
+
+EnumDefList:
+ EnumDefList EnumDef
+ {
+ pdebug("EnumDefList -> EnumDefList EnumDef");
+ $$ = $1;
+ $$->append($2);
+ }
+|
+ {
+ pdebug("EnumDefList -> ");
+ $$ = new t_enum(g_program);
+ y_enum_val = -1;
+ }
+
+EnumDef:
+ CaptureDocText EnumValue TypeAnnotations CommaOrSemicolonOptional
+ {
+ pdebug("EnumDef -> EnumValue");
+ $$ = $2;
+ if ($1 != NULL) {
+ $$->set_doc($1);
+ }
+ if ($3 != NULL) {
+ $$->annotations_ = $3->annotations_;
+ delete $3;
+ }
+ }
+
+EnumValue:
+ tok_identifier '=' tok_int_constant
+ {
+ pdebug("EnumValue -> tok_identifier = tok_int_constant");
+ if ($3 < INT32_MIN || $3 > INT32_MAX) {
+ // Note: this used to be just a warning. However, since thrift always
+ // treats enums as i32 values, I'm changing it to a fatal error.
+ // I doubt this will affect many people, but users who run into this
+ // will have to update their thrift files to manually specify the
+ // truncated i32 value that thrift has always been using anyway.
+ failure("64-bit value supplied for enum %s will be truncated.", $1);
+ }
+ y_enum_val = static_cast<int32_t>($3);
+ $$ = new t_enum_value($1, y_enum_val);
+ }
+ |
+ tok_identifier
+ {
+ pdebug("EnumValue -> tok_identifier");
+ validate_simple_identifier( $1);
+ if (y_enum_val == INT32_MAX) {
+ failure("enum value overflow at enum %s", $1);
+ }
+ ++y_enum_val;
+ $$ = new t_enum_value($1, y_enum_val);
+ }
+
+Senum:
+ tok_senum tok_identifier '{' SenumDefList '}' TypeAnnotations
+ {
+ pdebug("Senum -> tok_senum tok_identifier { SenumDefList }");
+ validate_simple_identifier( $2);
+ $$ = new t_typedef(g_program, $4, $2);
+ if ($6 != NULL) {
+ $$->annotations_ = $6->annotations_;
+ delete $6;
+ }
+ }
+
+SenumDefList:
+ SenumDefList SenumDef
+ {
+ pdebug("SenumDefList -> SenumDefList SenumDef");
+ $$ = $1;
+ $$->add_string_enum_val($2);
+ }
+|
+ {
+ pdebug("SenumDefList -> ");
+ $$ = new t_base_type("string", t_base_type::TYPE_STRING);
+ $$->set_string_enum(true);
+ }
+
+SenumDef:
+ tok_literal CommaOrSemicolonOptional
+ {
+ pdebug("SenumDef -> tok_literal");
+ $$ = $1;
+ }
+
+Const:
+ tok_const FieldType tok_identifier '=' ConstValue CommaOrSemicolonOptional
+ {
+ pdebug("Const -> tok_const FieldType tok_identifier = ConstValue");
+ if (g_parse_mode == PROGRAM) {
+ validate_simple_identifier( $3);
+ g_scope->resolve_const_value($5, $2);
+ $$ = new t_const($2, $3, $5);
+ validate_const_type($$);
+
+ g_scope->add_constant($3, $$);
+ if (g_parent_scope != NULL) {
+ g_parent_scope->add_constant(g_parent_prefix + $3, $$);
+ }
+ } else {
+ $$ = NULL;
+ }
+ }
+
+ConstValue:
+ tok_int_constant
+ {
+ pdebug("ConstValue => tok_int_constant");
+ $$ = new t_const_value();
+ $$->set_integer($1);
+ if (!g_allow_64bit_consts && ($1 < INT32_MIN || $1 > INT32_MAX)) {
+ pwarning(1, "64-bit constant \"%" PRIi64"\" may not work in all languages.\n", $1);
+ }
+ }
+| tok_dub_constant
+ {
+ pdebug("ConstValue => tok_dub_constant");
+ $$ = new t_const_value();
+ $$->set_double($1);
+ }
+| tok_literal
+ {
+ pdebug("ConstValue => tok_literal");
+ $$ = new t_const_value($1);
+ }
+| tok_identifier
+ {
+ pdebug("ConstValue => tok_identifier");
+ $$ = new t_const_value();
+ $$->set_identifier($1);
+ }
+| ConstList
+ {
+ pdebug("ConstValue => ConstList");
+ $$ = $1;
+ }
+| ConstMap
+ {
+ pdebug("ConstValue => ConstMap");
+ $$ = $1;
+ }
+
+ConstList:
+ '[' ConstListContents ']'
+ {
+ pdebug("ConstList => [ ConstListContents ]");
+ $$ = $2;
+ }
+
+ConstListContents:
+ ConstListContents ConstValue CommaOrSemicolonOptional
+ {
+ pdebug("ConstListContents => ConstListContents ConstValue CommaOrSemicolonOptional");
+ $$ = $1;
+ $$->add_list($2);
+ }
+|
+ {
+ pdebug("ConstListContents =>");
+ $$ = new t_const_value();
+ $$->set_list();
+ }
+
+ConstMap:
+ '{' ConstMapContents '}'
+ {
+ pdebug("ConstMap => { ConstMapContents }");
+ $$ = $2;
+ }
+
+ConstMapContents:
+ ConstMapContents ConstValue ':' ConstValue CommaOrSemicolonOptional
+ {
+ pdebug("ConstMapContents => ConstMapContents ConstValue CommaOrSemicolonOptional");
+ $$ = $1;
+ $$->add_map($2, $4);
+ }
+|
+ {
+ pdebug("ConstMapContents =>");
+ $$ = new t_const_value();
+ $$->set_map();
+ }
+
+StructHead:
+ tok_struct
+ {
+ $$ = struct_is_struct;
+ }
+| tok_union
+ {
+ $$ = struct_is_union;
+ }
+
+Struct:
+ StructHead tok_identifier XsdAll '{' FieldList '}' TypeAnnotations
+ {
+ pdebug("Struct -> tok_struct tok_identifier { FieldList }");
+ validate_simple_identifier( $2);
+ $5->set_xsd_all($3);
+ $5->set_union($1 == struct_is_union);
+ $$ = $5;
+ $$->set_name($2);
+ if ($7 != NULL) {
+ $$->annotations_ = $7->annotations_;
+ delete $7;
+ }
+ }
+
+XsdAll:
+ tok_xsd_all
+ {
+ $$ = true;
+ }
+|
+ {
+ $$ = false;
+ }
+
+XsdOptional:
+ tok_xsd_optional
+ {
+ $$ = true;
+ }
+|
+ {
+ $$ = false;
+ }
+
+XsdNillable:
+ tok_xsd_nillable
+ {
+ $$ = true;
+ }
+|
+ {
+ $$ = false;
+ }
+
+XsdAttributes:
+ tok_xsd_attrs '{' FieldList '}'
+ {
+ $$ = $3;
+ }
+|
+ {
+ $$ = NULL;
+ }
+
+Xception:
+ tok_xception tok_identifier '{' FieldList '}' TypeAnnotations
+ {
+ pdebug("Xception -> tok_xception tok_identifier { FieldList }");
+ validate_simple_identifier( $2);
+ $4->set_name($2);
+ $4->set_xception(true);
+ $$ = $4;
+ if ($6 != NULL) {
+ $$->annotations_ = $6->annotations_;
+ delete $6;
+ }
+ }
+
+Service:
+ tok_service tok_identifier Extends '{' FlagArgs FunctionList UnflagArgs '}' TypeAnnotations
+ {
+ pdebug("Service -> tok_service tok_identifier { FunctionList }");
+ validate_simple_identifier( $2);
+ $$ = $6;
+ $$->set_name($2);
+ $$->set_extends($3);
+ if ($9 != NULL) {
+ $$->annotations_ = $9->annotations_;
+ delete $9;
+ }
+ }
+
+FlagArgs:
+ {
+ g_arglist = 1;
+ }
+
+UnflagArgs:
+ {
+ g_arglist = 0;
+ }
+
+Extends:
+ tok_extends tok_identifier
+ {
+ pdebug("Extends -> tok_extends tok_identifier");
+ $$ = NULL;
+ if (g_parse_mode == PROGRAM) {
+ $$ = g_scope->get_service($2);
+ if ($$ == NULL) {
+ yyerror("Service \"%s\" has not been defined.", $2);
+ exit(1);
+ }
+ }
+ }
+|
+ {
+ $$ = NULL;
+ }
+
+FunctionList:
+ FunctionList Function
+ {
+ pdebug("FunctionList -> FunctionList Function");
+ $$ = $1;
+ $1->add_function($2);
+ }
+|
+ {
+ pdebug("FunctionList -> ");
+ $$ = new t_service(g_program);
+ }
+
+Function:
+ CaptureDocText Oneway FunctionType tok_identifier '(' FieldList ')' Throws TypeAnnotations CommaOrSemicolonOptional
+ {
+ validate_simple_identifier( $4);
+ $6->set_name(std::string($4) + "_args");
+ $$ = new t_function($3, $4, $6, $8, $2);
+ if ($1 != NULL) {
+ $$->set_doc($1);
+ }
+ if ($9 != NULL) {
+ $$->annotations_ = $9->annotations_;
+ delete $9;
+ }
+ }
+
+Oneway:
+ tok_oneway
+ {
+ $$ = true;
+ }
+|
+ {
+ $$ = false;
+ }
+
+Throws:
+ tok_throws '(' FieldList ')'
+ {
+ pdebug("Throws -> tok_throws ( FieldList )");
+ $$ = $3;
+ if (g_parse_mode == PROGRAM && !validate_throws($$)) {
+ yyerror("Throws clause may not contain non-exception types");
+ exit(1);
+ }
+ }
+|
+ {
+ $$ = new t_struct(g_program);
+ }
+
+FieldList:
+ FieldList Field
+ {
+ pdebug("FieldList -> FieldList , Field");
+ $$ = $1;
+ if (!($$->append($2))) {
+ yyerror("\"%d: %s\" - field identifier/name has already been used", $2->get_key(), $2->get_name().c_str());
+ exit(1);
+ }
+ }
+|
+ {
+ pdebug("FieldList -> ");
+ y_field_val = -1;
+ $$ = new t_struct(g_program);
+ }
+
+Field:
+ CaptureDocText FieldIdentifier FieldRequiredness FieldType FieldReference tok_identifier FieldValue XsdOptional XsdNillable XsdAttributes TypeAnnotations CommaOrSemicolonOptional
+ {
+ pdebug("tok_int_constant : Field -> FieldType tok_identifier");
+ if ($2.auto_assigned) {
+ pwarning(1, "No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", $6);
+ if (g_strict >= 192) {
+ yyerror("Implicit field keys are deprecated and not allowed with -strict");
+ exit(1);
+ }
+ }
+ validate_simple_identifier($6);
+ $$ = new t_field($4, $6, $2.value);
+ $$->set_reference($5);
+ $$->set_req($3);
+ if ($7 != NULL) {
+ g_scope->resolve_const_value($7, $4);
+ validate_field_value($$, $7);
+ $$->set_value($7);
+ }
+ $$->set_xsd_optional($8);
+ $$->set_xsd_nillable($9);
+ if ($1 != NULL) {
+ $$->set_doc($1);
+ }
+ if ($10 != NULL) {
+ $$->set_xsd_attrs($10);
+ }
+ if ($11 != NULL) {
+ $$->annotations_ = $11->annotations_;
+ delete $11;
+ }
+ }
+
+FieldIdentifier:
+ tok_int_constant ':'
+ {
+ if ($1 <= 0) {
+ if (g_allow_neg_field_keys) {
+ /*
+ * g_allow_neg_field_keys exists to allow users to add explicitly
+ * specified key values to old .thrift files without breaking
+ * protocol compatibility.
+ */
+ if ($1 != y_field_val) {
+ /*
+ * warn if the user-specified negative value isn't what
+ * thrift would have auto-assigned.
+ */
+ pwarning(1, "Nonpositive field key (%" PRIi64") differs from what would be "
+ "auto-assigned by thrift (%d).\n", $1, y_field_val);
+ }
+ /*
+ * Leave $1 as-is, and update y_field_val to be one less than $1.
+ * The FieldList parsing will catch any duplicate key values.
+ */
+ y_field_val = static_cast<int32_t>($1 - 1);
+ $$.value = static_cast<int32_t>($1);
+ $$.auto_assigned = false;
+ } else {
+ pwarning(1, "Nonpositive value (%d) not allowed as a field key.\n",
+ $1);
+ $$.value = y_field_val--;
+ $$.auto_assigned = true;
+ }
+ } else {
+ $$.value = static_cast<int32_t>($1);
+ $$.auto_assigned = false;
+ }
+ if( (SHRT_MIN > $$.value) || ($$.value > SHRT_MAX)) {
+ pwarning(1, "Field key (%d) exceeds allowed range (%d..%d).\n",
+ $$.value, SHRT_MIN, SHRT_MAX);
+ }
+ }
+|
+ {
+ $$.value = y_field_val--;
+ $$.auto_assigned = true;
+ if( (SHRT_MIN > $$.value) || ($$.value > SHRT_MAX)) {
+ pwarning(1, "Field key (%d) exceeds allowed range (%d..%d).\n",
+ $$.value, SHRT_MIN, SHRT_MAX);
+ }
+ }
+
+FieldReference:
+ tok_reference
+ {
+ $$ = true;
+ }
+|
+ {
+ $$ = false;
+ }
+
+FieldRequiredness:
+ tok_required
+ {
+ $$ = t_field::T_REQUIRED;
+ }
+| tok_optional
+ {
+ if (g_arglist) {
+ if (g_parse_mode == PROGRAM) {
+ pwarning(1, "optional keyword is ignored in argument lists.\n");
+ }
+ $$ = t_field::T_OPT_IN_REQ_OUT;
+ } else {
+ $$ = t_field::T_OPTIONAL;
+ }
+ }
+|
+ {
+ $$ = t_field::T_OPT_IN_REQ_OUT;
+ }
+
+FieldValue:
+ '=' ConstValue
+ {
+ if (g_parse_mode == PROGRAM) {
+ $$ = $2;
+ } else {
+ $$ = NULL;
+ }
+ }
+|
+ {
+ $$ = NULL;
+ }
+
+FunctionType:
+ FieldType
+ {
+ pdebug("FunctionType -> FieldType");
+ $$ = $1;
+ }
+| tok_void
+ {
+ pdebug("FunctionType -> tok_void");
+ $$ = g_type_void;
+ }
+
+FieldType:
+ tok_identifier
+ {
+ pdebug("FieldType -> tok_identifier");
+ if (g_parse_mode == INCLUDES) {
+ // Ignore identifiers in include mode
+ $$ = NULL;
+ } else {
+ // Lookup the identifier in the current scope
+ $$ = g_scope->get_type($1);
+ if ($$ == NULL) {
+ /*
+ * Either this type isn't yet declared, or it's never
+ declared. Either way allow it and we'll figure it out
+ during generation.
+ */
+ $$ = new t_typedef(g_program, $1, true);
+ }
+ }
+ }
+| BaseType
+ {
+ pdebug("FieldType -> BaseType");
+ $$ = $1;
+ }
+| ContainerType
+ {
+ pdebug("FieldType -> ContainerType");
+ $$ = $1;
+ }
+
+BaseType: SimpleBaseType TypeAnnotations
+ {
+ pdebug("BaseType -> SimpleBaseType TypeAnnotations");
+ if ($2 != NULL) {
+ $$ = new t_base_type(*static_cast<t_base_type*>($1));
+ $$->annotations_ = $2->annotations_;
+ delete $2;
+ } else {
+ $$ = $1;
+ }
+ }
+
+SimpleBaseType:
+ tok_string
+ {
+ pdebug("BaseType -> tok_string");
+ $$ = g_type_string;
+ }
+| tok_binary
+ {
+ pdebug("BaseType -> tok_binary");
+ $$ = g_type_binary;
+ }
+| tok_slist
+ {
+ pdebug("BaseType -> tok_slist");
+ $$ = g_type_slist;
+ }
+| tok_bool
+ {
+ pdebug("BaseType -> tok_bool");
+ $$ = g_type_bool;
+ }
+| tok_i8
+ {
+ pdebug("BaseType -> tok_i8");
+ $$ = g_type_i8;
+ }
+| tok_i16
+ {
+ pdebug("BaseType -> tok_i16");
+ $$ = g_type_i16;
+ }
+| tok_i32
+ {
+ pdebug("BaseType -> tok_i32");
+ $$ = g_type_i32;
+ }
+| tok_i64
+ {
+ pdebug("BaseType -> tok_i64");
+ $$ = g_type_i64;
+ }
+| tok_double
+ {
+ pdebug("BaseType -> tok_double");
+ $$ = g_type_double;
+ }
+
+ContainerType: SimpleContainerType TypeAnnotations
+ {
+ pdebug("ContainerType -> SimpleContainerType TypeAnnotations");
+ $$ = $1;
+ if ($2 != NULL) {
+ $$->annotations_ = $2->annotations_;
+ delete $2;
+ }
+ }
+
+SimpleContainerType:
+ MapType
+ {
+ pdebug("SimpleContainerType -> MapType");
+ $$ = $1;
+ }
+| SetType
+ {
+ pdebug("SimpleContainerType -> SetType");
+ $$ = $1;
+ }
+| ListType
+ {
+ pdebug("SimpleContainerType -> ListType");
+ $$ = $1;
+ }
+
+MapType:
+ tok_map CppType '<' FieldType ',' FieldType '>'
+ {
+ pdebug("MapType -> tok_map <FieldType, FieldType>");
+ $$ = new t_map($4, $6);
+ if ($2 != NULL) {
+ ((t_container*)$$)->set_cpp_name(std::string($2));
+ }
+ }
+
+SetType:
+ tok_set CppType '<' FieldType '>'
+ {
+ pdebug("SetType -> tok_set<FieldType>");
+ $$ = new t_set($4);
+ if ($2 != NULL) {
+ ((t_container*)$$)->set_cpp_name(std::string($2));
+ }
+ }
+
+ListType:
+ tok_list '<' FieldType '>' CppType
+ {
+ pdebug("ListType -> tok_list<FieldType>");
+ check_for_list_of_bytes($3);
+ $$ = new t_list($3);
+ if ($5 != NULL) {
+ ((t_container*)$$)->set_cpp_name(std::string($5));
+ }
+ }
+
+CppType:
+ tok_cpp_type tok_literal
+ {
+ $$ = $2;
+ }
+|
+ {
+ $$ = NULL;
+ }
+
+TypeAnnotations:
+ '(' TypeAnnotationList ')'
+ {
+ pdebug("TypeAnnotations -> ( TypeAnnotationList )");
+ $$ = $2;
+ }
+|
+ {
+ $$ = NULL;
+ }
+
+TypeAnnotationList:
+ TypeAnnotationList TypeAnnotation
+ {
+ pdebug("TypeAnnotationList -> TypeAnnotationList , TypeAnnotation");
+ $$ = $1;
+ $$->annotations_[$2->key] = $2->val;
+ delete $2;
+ }
+|
+ {
+ /* Just use a dummy structure to hold the annotations. */
+ $$ = new t_struct(g_program);
+ }
+
+TypeAnnotation:
+ tok_identifier TypeAnnotationValue CommaOrSemicolonOptional
+ {
+ pdebug("TypeAnnotation -> TypeAnnotationValue");
+ $$ = new t_annotation;
+ $$->key = $1;
+ $$->val = $2;
+ }
+
+TypeAnnotationValue:
+ '=' tok_literal
+ {
+ pdebug("TypeAnnotationValue -> = tok_literal");
+ $$ = $2;
+ }
+|
+ {
+ pdebug("TypeAnnotationValue ->");
+ $$ = strdup("1");
+ }
+
+%%
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/version.h.in b/src/jaegertracing/thrift/compiler/cpp/src/thrift/version.h.in
new file mode 100644
index 000000000..aef076f7f
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/version.h.in
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef _THRIFT_VERSION_H_
+#define _THRIFT_VERSION_H_ 1
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#define THRIFT_VERSION "@PACKAGE_VERSION@"
+
+#endif // _THRIFT_VERSION_H_
diff --git a/src/jaegertracing/thrift/compiler/cpp/src/thrift/windows/config.h b/src/jaegertracing/thrift/compiler/cpp/src/thrift/windows/config.h
new file mode 100644
index 000000000..6ba4f9ae6
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/src/thrift/windows/config.h
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef _THRIFT_WINDOWS_CONFIG_H_
+#define _THRIFT_WINDOWS_CONFIG_H_ 1
+
+#if defined(_MSC_VER) && (_MSC_VER > 1200)
+#pragma once
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#error "This is a Windows header only"
+#endif
+
+#include <io.h>
+#include <stdlib.h>
+#include <direct.h>
+
+#define strtoll(begin_ptr, end_ptr, length) _strtoi64(begin_ptr, end_ptr, length)
+
+#ifndef PRIu64
+#define PRIu64 "I64u"
+#endif
+
+#ifndef PRIi64
+#define PRIi64 "I64i"
+#endif
+// squelch deprecation warnings
+#pragma warning(disable : 4996)
+// squelch bool conversion performance warning
+#pragma warning(disable : 4800)
+
+#include <cstdint>
+
+#endif // _THRIFT_WINDOWS_CONFIG_H_
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/CMakeLists.txt b/src/jaegertracing/thrift/compiler/cpp/test/CMakeLists.txt
new file mode 100644
index 000000000..2bc7e9e69
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/CMakeLists.txt
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+# Unit tests for the compiler still require boost
+include(BoostMacros)
+REQUIRE_BOOST_HEADERS()
+set(BOOST_COMPONENTS unit_test_framework)
+REQUIRE_BOOST_LIBRARIES(BOOST_COMPONENTS)
+
+file(GLOB KEYWORD_SAMPLES "${CMAKE_CURRENT_SOURCE_DIR}/keyword-samples/*.thrift")
+foreach(LANG ${thrift_compiler_LANGS})
+ foreach(SAMPLE ${KEYWORD_SAMPLES})
+ get_filename_component(FILENAME ${SAMPLE} NAME_WE)
+ add_test(NAME "${LANG}_${FILENAME}"
+ COMMAND thrift-compiler --gen ${LANG} ${SAMPLE})
+ set_tests_properties("${LANG}_${FILENAME}" PROPERTIES
+ PASS_REGULAR_EXPRESSION "Cannot use reserved language keyword")
+ endforeach()
+endforeach()
+
+
+find_package(PythonInterp QUIET)
+if(PYTHONINTERP_FOUND)
+ add_test(NAME StalenessCheckTest COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/compiler/staleness_check.py ${THRIFT_COMPILER})
+else()
+ message(WARNING "Skipping StalenessCheckTest as there is no python interpreter available.")
+endif()
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/Makefile.am b/src/jaegertracing/thrift/compiler/cpp/test/Makefile.am
new file mode 100644
index 000000000..801f9d85e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/Makefile.am
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+#
+# Contains some contributions under the Thrift Software License.
+# Please see doc/old-thrift-license.txt in the Thrift distribution for
+# details.
+
+AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc
+
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/compiler/cpp/src
+AM_LDFLAGS = $(BOOST_LDFLAGS)
+AM_CXXFLAGS = -Wall -Wextra -pedantic
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/bincat.sh b/src/jaegertracing/thrift/compiler/cpp/test/bincat.sh
new file mode 100755
index 000000000..c7f90785e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/bincat.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec /bin/cat
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/compiler/Included.thrift b/src/jaegertracing/thrift/compiler/cpp/test/compiler/Included.thrift
new file mode 100644
index 000000000..ce84ab6b9
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/compiler/Included.thrift
@@ -0,0 +1,18 @@
+const string foo = "bar"
+
+struct a_struct {
+ 1: bool im_true,
+ 2: bool im_false,
+ 3: i8 a_bite,
+ 4: i16 integer16,
+ 5: i32 integer32,
+ 6: i64 integer64,
+ 7: double double_precision,
+ 8: string some_characters,
+ 9: string zomg_unicode,
+ 10: bool what_who,
+}
+
+service AService {
+ i32 a_procedure(1: i32 arg)
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/compiler/Including.thrift b/src/jaegertracing/thrift/compiler/cpp/test/compiler/Including.thrift
new file mode 100644
index 000000000..677af7bb2
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/compiler/Including.thrift
@@ -0,0 +1,7 @@
+include "Included.thrift"
+
+const string s = "string"
+
+struct BStruct {
+ 1: Included.a_struct one_of_each
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/compiler/Single.thrift b/src/jaegertracing/thrift/compiler/cpp/test/compiler/Single.thrift
new file mode 100644
index 000000000..2ec301f50
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/compiler/Single.thrift
@@ -0,0 +1 @@
+const string foo = "bar"
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/compiler/staleness_check.py b/src/jaegertracing/thrift/compiler/cpp/test/compiler/staleness_check.py
new file mode 100755
index 000000000..5b11dff9e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/compiler/staleness_check.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 __future__ import print_function
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import time
+import unittest
+
+
+class TestStalenessCheck(unittest.TestCase):
+
+ CURRENT_DIR_PATH = os.path.dirname(os.path.realpath(__file__))
+ THRIFT_EXECUTABLE_PATH = None
+ SINGLE_THRIFT_FILE_PATH = os.path.join(CURRENT_DIR_PATH, "Single.thrift")
+ INCLUDING_THRIFT_FILE_PATH = os.path.join(CURRENT_DIR_PATH, "Including.thrift")
+ INCLUDED_THRIFT_FILE_PATH = os.path.join(CURRENT_DIR_PATH, "Included.thrift")
+
+ def test_staleness_check_of_single_thrift_file_without_changed_output(self):
+ temp_dir = tempfile.mkdtemp(dir=TestStalenessCheck.CURRENT_DIR_PATH)
+
+ command = [TestStalenessCheck.THRIFT_EXECUTABLE_PATH, "-gen", "cpp", "-o", temp_dir]
+ command += [TestStalenessCheck.SINGLE_THRIFT_FILE_PATH]
+ subprocess.call(command)
+
+ used_file_path = os.path.join(temp_dir, "gen-cpp", "Single_constants.cpp")
+
+ first_modification_time = os.path.getmtime(os.path.join(used_file_path))
+
+ time.sleep(0.1)
+
+ subprocess.call(command)
+
+ second_modification_time = os.path.getmtime(used_file_path)
+
+ self.assertEqual(second_modification_time, first_modification_time)
+
+ shutil.rmtree(temp_dir, ignore_errors=True)
+
+ def test_staleness_check_of_single_thrift_file_with_changed_output(self):
+ temp_dir = tempfile.mkdtemp(dir=TestStalenessCheck.CURRENT_DIR_PATH)
+
+ command = [TestStalenessCheck.THRIFT_EXECUTABLE_PATH, "-gen", "cpp", "-o", temp_dir]
+ command += [TestStalenessCheck.SINGLE_THRIFT_FILE_PATH]
+ subprocess.call(command)
+
+ used_file_path = os.path.join(temp_dir, "gen-cpp", "Single_constants.cpp")
+
+ first_modification_time = os.path.getmtime(os.path.join(used_file_path))
+ used_file = open(used_file_path, "r")
+ first_contents = used_file.read()
+ used_file.close()
+
+ used_file = open(used_file_path, "a")
+ used_file.write("\n/* This is a comment */\n")
+ used_file.close()
+
+ time.sleep(0.1)
+
+ subprocess.call(command)
+
+ second_modification_time = os.path.getmtime(used_file_path)
+ used_file = open(used_file_path, "r")
+ second_contents = used_file.read()
+ used_file.close()
+
+ self.assertGreater(second_modification_time, first_modification_time)
+ self.assertEqual(first_contents, second_contents)
+
+ shutil.rmtree(temp_dir, ignore_errors=True)
+
+ def test_staleness_check_of_included_file(self):
+ temp_dir = tempfile.mkdtemp(dir=TestStalenessCheck.CURRENT_DIR_PATH)
+
+ temp_included_file_path = os.path.join(temp_dir, "Included.thrift")
+ temp_including_file_path = os.path.join(temp_dir, "Including.thrift")
+
+ shutil.copy2(TestStalenessCheck.INCLUDED_THRIFT_FILE_PATH, temp_included_file_path)
+ shutil.copy2(TestStalenessCheck.INCLUDING_THRIFT_FILE_PATH, temp_including_file_path)
+
+ command = [TestStalenessCheck.THRIFT_EXECUTABLE_PATH, "-gen", "cpp", "-recurse", "-o", temp_dir]
+ command += [temp_including_file_path]
+
+ subprocess.call(command)
+
+ included_constants_cpp_file_path = os.path.join(temp_dir, "gen-cpp", "Included_constants.cpp")
+ including_constants_cpp_file_path = os.path.join(temp_dir, "gen-cpp", "Including_constants.cpp")
+
+ included_constants_cpp_first_modification_time = os.path.getmtime(included_constants_cpp_file_path)
+ including_constants_cpp_first_modification_time = os.path.getmtime(including_constants_cpp_file_path)
+
+ temp_included_file = open(temp_included_file_path, "a")
+ temp_included_file.write("\nconst i32 an_integer = 42\n")
+ temp_included_file.close()
+
+ time.sleep(0.1)
+
+ subprocess.call(command)
+
+ included_constants_cpp_second_modification_time = os.path.getmtime(included_constants_cpp_file_path)
+ including_constants_cpp_second_modification_time = os.path.getmtime(including_constants_cpp_file_path)
+
+ self.assertGreater(
+ included_constants_cpp_second_modification_time, included_constants_cpp_first_modification_time)
+ self.assertEqual(
+ including_constants_cpp_first_modification_time, including_constants_cpp_second_modification_time)
+
+ shutil.rmtree(temp_dir, ignore_errors=True)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+ suite.addTest(loader.loadTestsFromTestCase(TestStalenessCheck))
+ return suite
+
+
+if __name__ == "__main__":
+ # The path of Thrift compiler is passed as an argument to the test script.
+ # Remove it to not confuse the unit testing framework
+ TestStalenessCheck.THRIFT_EXECUTABLE_PATH = sys.argv[-1]
+ del sys.argv[-1]
+ unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/const1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/const1_return.thrift
new file mode 100644
index 000000000..735e4acd8
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/const1_return.thrift
@@ -0,0 +1 @@
+const bool return = 0
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum1_return.thrift
new file mode 100644
index 000000000..6d834e1da
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum1_return.thrift
@@ -0,0 +1,2 @@
+enum return {
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum2_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum2_return.thrift
new file mode 100644
index 000000000..a2caa8e14
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/enum2_return.thrift
@@ -0,0 +1,3 @@
+enum enum_name {
+ return
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception1_return.thrift
new file mode 100644
index 000000000..eadb33834
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception1_return.thrift
@@ -0,0 +1 @@
+exception return {}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception2_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception2_return.thrift
new file mode 100644
index 000000000..493c35297
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/exception2_return.thrift
@@ -0,0 +1,3 @@
+exception exception_name {
+ 1: required i8 return
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service1_return.thrift
new file mode 100644
index 000000000..5286a3691
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service1_return.thrift
@@ -0,0 +1 @@
+service return {}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service2_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service2_return.thrift
new file mode 100644
index 000000000..6f7331da0
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service2_return.thrift
@@ -0,0 +1,3 @@
+service service_name {
+ bool function_name(1: i32 return)
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service3_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service3_return.thrift
new file mode 100644
index 000000000..c6dd946fd
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service3_return.thrift
@@ -0,0 +1,3 @@
+service service_name {
+ void return()
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service4_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service4_return.thrift
new file mode 100644
index 000000000..d0787dfde
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/service4_return.thrift
@@ -0,0 +1,5 @@
+exception exception_name {}
+
+service service_name {
+ void function_name() throws ( 1: exception_name return)
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct1_return.thrift
new file mode 100644
index 000000000..c82b8b9ca
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct1_return.thrift
@@ -0,0 +1 @@
+struct return {}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct2_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct2_return.thrift
new file mode 100644
index 000000000..a0700d101
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/struct2_return.thrift
@@ -0,0 +1,3 @@
+struct struct_name {
+ 1: required bool return = 1
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/typedef1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/typedef1_return.thrift
new file mode 100644
index 000000000..f159bb880
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/typedef1_return.thrift
@@ -0,0 +1 @@
+typedef bool return
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union1_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union1_return.thrift
new file mode 100644
index 000000000..368df1383
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union1_return.thrift
@@ -0,0 +1 @@
+union return {}
diff --git a/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union2_return.thrift b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union2_return.thrift
new file mode 100644
index 000000000..9719d1e40
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/test/keyword-samples/union2_return.thrift
@@ -0,0 +1,3 @@
+union union_name {
+ 1: optional bool return=1
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/CMakeLists.txt b/src/jaegertracing/thrift/compiler/cpp/tests/CMakeLists.txt
new file mode 100644
index 000000000..fde9073ca
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/CMakeLists.txt
@@ -0,0 +1,153 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+cmake_minimum_required(VERSION 2.8.12)
+
+project(thrift_compiler_tests)
+
+set(THRIFT_COMPILER_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/..
+)
+
+# don't generate ZERO_CHECK
+set(CMAKE_SUPPRESS_REGENERATION true)
+
+configure_file(${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h)
+if(MSVC)
+ # The winflexbison generator outputs some macros that conflict with the Visual Studio 2010 copy of stdint.h
+ # This might be fixed in later versions of Visual Studio, but an easy solution is to include stdint.h first
+ if(HAVE_STDINT_H)
+ add_definitions(-D__STDC_LIMIT_MACROS)
+ add_definitions(/FI"stdint.h")
+ endif(HAVE_STDINT_H)
+endif()
+
+find_package(FLEX REQUIRED)
+find_package(BISON REQUIRED)
+
+# create directory for thrifty and thriftl
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/thrift/)
+
+# Create flex and bison files and build the lib parse static library
+BISON_TARGET(thrifty ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/thrifty.yy ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.cc)
+FLEX_TARGET(thriftl ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/thriftl.ll ${CMAKE_CURRENT_BINARY_DIR}/thrift/thriftl.cc)
+ADD_FLEX_BISON_DEPENDENCY(thriftl thrifty)
+
+set(parse_SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/thriftl.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.hh
+)
+
+add_library(parse STATIC ${parse_SOURCES})
+
+# Thrift compiler tests
+set(thrift_compiler_tests
+)
+
+# you can add some files manually there
+set(thrift_compiler_tests_manual_SOURCES
+ # tests file to avoid main in every test file
+ ${CMAKE_CURRENT_SOURCE_DIR}/tests_main.cc
+)
+
+# set variable for tests sources - will be filled later
+set(thrift_compiler_tests_SOURCES
+)
+
+set(thrift_compiler_SOURCES
+ ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/logging.cc # we use logging instead of main to avoid breaking compillation (2 main v)
+ ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/audit/t_audit.cpp
+ ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/common.cc
+ ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/generate/t_generator.cc
+ ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/parse/t_typedef.cc
+ ${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/parse/parse.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h
+)
+
+# This macro adds an option THRIFT_COMPILER_${NAME}
+# that allows enabling or disabling certain languages
+macro(THRIFT_ADD_COMPILER name description initial)
+ string(TOUPPER "THRIFT_COMPILER_${name}" enabler)
+ set(src "${THRIFT_COMPILER_SOURCE_DIR}/src/thrift/generate/t_${name}_generator.cc")
+ option(${enabler} ${description} ${initial})
+ if(${enabler})
+ list(APPEND thrift_compiler_SOURCES ${src})
+ file(GLOB thrift_compiler_tests_SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.c*"
+ "${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.thrift"
+ )
+ endif()
+endmacro()
+
+# The following compiler with unit tests can be enabled or disabled
+THRIFT_ADD_COMPILER(as3 "Enable compiler for ActionScript 3" OFF)
+THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" OFF)
+THRIFT_ADD_COMPILER(cl "Enable compiler for Common LISP" OFF)
+THRIFT_ADD_COMPILER(cpp "Enable compiler for C++" OFF)
+THRIFT_ADD_COMPILER(csharp "Enable compiler for C#" OFF)
+THRIFT_ADD_COMPILER(d "Enable compiler for D" OFF)
+THRIFT_ADD_COMPILER(dart "Enable compiler for Dart" OFF)
+THRIFT_ADD_COMPILER(delphi "Enable compiler for Delphi" OFF)
+THRIFT_ADD_COMPILER(erl "Enable compiler for Erlang" OFF)
+THRIFT_ADD_COMPILER(go "Enable compiler for Go" OFF)
+THRIFT_ADD_COMPILER(gv "Enable compiler for GraphViz" OFF)
+THRIFT_ADD_COMPILER(haxe "Enable compiler for Haxe" OFF)
+THRIFT_ADD_COMPILER(hs "Enable compiler for Haskell" OFF)
+THRIFT_ADD_COMPILER(html "Enable compiler for HTML Documentation" OFF)
+THRIFT_ADD_COMPILER(java "Enable compiler for Java" OFF)
+THRIFT_ADD_COMPILER(javame "Enable compiler for Java ME" OFF)
+THRIFT_ADD_COMPILER(js "Enable compiler for JavaScript" OFF)
+THRIFT_ADD_COMPILER(json "Enable compiler for JSON" OFF)
+THRIFT_ADD_COMPILER(lua "Enable compiler for Lua" OFF)
+THRIFT_ADD_COMPILER(netcore "Enable compiler for .NET Core" ON)
+THRIFT_ADD_COMPILER(ocaml "Enable compiler for OCaml" OFF)
+THRIFT_ADD_COMPILER(perl "Enable compiler for Perl" OFF)
+THRIFT_ADD_COMPILER(php "Enable compiler for PHP" OFF)
+THRIFT_ADD_COMPILER(py "Enable compiler for Python 2.0" OFF)
+THRIFT_ADD_COMPILER(rb "Enable compiler for Ruby" OFF)
+THRIFT_ADD_COMPILER(rs "Enable compiler for Rust" OFF)
+THRIFT_ADD_COMPILER(st "Enable compiler for Smalltalk" OFF)
+THRIFT_ADD_COMPILER(swift "Enable compiler for Swift" OFF)
+THRIFT_ADD_COMPILER(xml "Enable compiler for XML" OFF)
+THRIFT_ADD_COMPILER(xsd "Enable compiler for XSD" OFF)
+
+# Thrift is looking for include files in the src directory
+# we also add the current binary directory for generated files
+include_directories(${CMAKE_CURRENT_BINARY_DIR} ${THRIFT_COMPILER_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/catch)
+
+add_library(thrift_compiler ${thrift_compiler_SOURCES})
+
+#link parse lib to thrift_compiler lib
+target_link_libraries(thrift_compiler parse)
+
+# add tests executable
+add_executable(thrift_compiler_tests ${thrift_compiler_tests_manual_SOURCES} ${thrift_compiler_tests_SOURCES})
+
+# if generates for Visual Studio set thrift_compiler_tests as default project
+if(MSVC)
+ set_property(TARGET thrift_compiler_tests PROPERTY VS_STARTUP_PROJECT thrift_compiler_tests)
+endif()
+
+set_target_properties(thrift_compiler_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin/)
+set_target_properties(thrift_compiler_tests PROPERTIES OUTPUT_NAME thrift_compiler_tests)
+
+target_link_libraries(thrift_compiler_tests thrift_compiler)
+
+enable_testing()
+add_test(NAME ThriftTests COMMAND thrift_compiler_tests)
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/README.md b/src/jaegertracing/thrift/compiler/cpp/tests/README.md
new file mode 100644
index 000000000..27be491cb
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/README.md
@@ -0,0 +1,88 @@
+# Build and run compiler tests using CMake
+
+<!-- TOC -->
+
+- [Build and run compiler tests using CMake](#build-and-run-compiler-tests-using-cmake)
+ - [General information](#general-information)
+ - [How to add your tests](#how-to-add-your-tests)
+ - [Build and run tests on Unix-like systems](#build-and-run-tests-on-unix-like-systems)
+ - [Prerequisites:](#prerequisites)
+ - [Build and run test with CMake](#build-and-run-test-with-cmake)
+ - [Build and run tests on Windows](#build-and-run-tests-on-windows)
+ - [Prerequisites:](#prerequisites-1)
+ - [Generation of VS project with CMake, build and run on Windows](#generation-of-vs-project-with-cmake-build-and-run-on-windows)
+
+<!-- /TOC -->
+
+## General information
+
+Added generic way to cover code by tests for many languages (you just need to make a correct header file for generator for your language - example in **netcore** implementation)
+
+At current moment these tests use free Catch library (https://github.com/catchorg/Catch2/tree/Catch1.x) for easy test creation and usage.
+Decision to use it was because of simplicity, easy usage, one header file to use, stable community and growing interest (https://cpp.libhunt.com/project/googletest-google/vs/catch?rel=cmp-cmp)
+
+Also, maybe, later it will be migrated to Catch2 (https://github.com/philsquared/Catch) - depends on need to support legacy compilers (c++98)
+
+## How to add your tests
+
+- Open **CMakeLists.txt**
+- Set **On** to call of **THRIFT_ADD_COMPILER** for your language
+
+``` cmake
+THRIFT_ADD_COMPILER(netcore "Enable compiler for .NET Core" ON)
+```
+
+- Create folder with name specified in list of languages in **CMakeLists.txt**
+- Create tests in folder for your language (with extensions like *.c* - cc, cpp, etc)
+ - Don't forget to add include of catch.hpp in your test file
+ ``` C
+ #include "../catch/catch.hpp"
+ ```
+
+- If you need - add files manually to **thrift_compiler_tests_manual_SOURCES** in **CMakeLists.txt** similar to
+
+``` cmake
+# you can add some files manually there
+set(thrift_compiler_tests_manual_SOURCES
+ # tests file to avoid main in every test file
+ ${CMAKE_CURRENT_SOURCE_DIR}/tests_main.cc
+)
+```
+
+- Run **cmake** with arguments for your environment and compiler
+- Enjoy
+
+## Build and run tests on Unix-like systems
+
+### Prerequisites:
+- Install CMake - <https://cmake.org/download/>
+- Install winflexbison - <https://sourceforge.net/projects/winflexbison/>
+
+### Build and run test with CMake
+
+- Run commands in command line in current directory:
+
+```
+mkdir cmake-vs && cd cmake-vs
+cmake ..
+cmake --build .
+ctest -C Debug -V
+```
+
+## Build and run tests on Windows
+
+### Prerequisites:
+- Install CMake - <https://cmake.org/download/>
+- Install winflexbison - <https://sourceforge.net/projects/winflexbison/>
+- Install VS2017 Community Edition - <https://www.visualstudio.com/vs/whatsnew/> (ensure that you installed workload "Desktop Development with C++" for VS2017)
+
+### Generation of VS project with CMake, build and run on Windows
+- Run commands in command line in current directory (ensure that VS installed):
+
+```
+mkdir cmake-vs
+cd cmake-vs
+cmake ..
+cmake --build .
+ctest -C Debug -V
+``` \ No newline at end of file
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/catch/catch.hpp b/src/jaegertracing/thrift/compiler/cpp/tests/catch/catch.hpp
new file mode 100644
index 000000000..33d037e55
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/catch/catch.hpp
@@ -0,0 +1,11508 @@
+/*
+ * Catch v1.9.4
+ * Generated: 2017-05-16 13:51:55.506519
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic ignored "-Wglobal-constructors"
+# pragma clang diagnostic ignored "-Wvariadic-macros"
+# pragma clang diagnostic ignored "-Wc99-extensions"
+# pragma clang diagnostic ignored "-Wunused-variable"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wc++98-compat"
+# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wvariadic-macros"
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wparentheses"
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __cplusplus
+
+# if __cplusplus >= 201103L
+# define CATCH_CPP11_OR_GREATER
+# endif
+
+# if __cplusplus >= 201402L
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+#endif
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# if defined(CATCH_CPP11_OR_GREATER)
+# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__)
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
+#if (_MSC_VER >= 1600)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+
+#endif
+
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+ ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \
+ ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(CATCH_CPP11_OR_GREATER)
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+# endif
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+# endif
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+# define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#endif
+// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for
+// analytics) because, at time of writing, __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+# define CATCH_NOEXCEPT noexcept
+# define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+# define CATCH_NOEXCEPT throw()
+# define CATCH_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+# define CATCH_NULL nullptr
+#else
+# define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+# define CATCH_OVERRIDE override
+#else
+# define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+# define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+ struct IConfig;
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+#else
+ NonCopyable( NonCopyable const& info );
+ NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+ protected:
+ NonCopyable() {}
+ virtual ~NonCopyable();
+ };
+
+ class SafeBool {
+ public:
+ typedef void (SafeBool::*type)() const;
+
+ static type makeSafe( bool value ) {
+ return value ? &SafeBool::trueValue : 0;
+ }
+ private:
+ void trueValue() const {}
+ };
+
+ template<typename ContainerT>
+ inline void deleteAll( ContainerT& container ) {
+ typename ContainerT::const_iterator it = container.begin();
+ typename ContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete *it;
+ }
+ template<typename AssociativeContainerT>
+ inline void deleteAllValues( AssociativeContainerT& container ) {
+ typename AssociativeContainerT::const_iterator it = container.begin();
+ typename AssociativeContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete it->second;
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool startsWith( std::string const& s, char prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool endsWith( std::string const& s, char suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ std::string trim( std::string const& str );
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo();
+ SourceLineInfo( char const* _file, std::size_t _line );
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SourceLineInfo(SourceLineInfo const& other) = default;
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+# endif
+ bool empty() const;
+ bool operator == ( SourceLineInfo const& other ) const;
+ bool operator < ( SourceLineInfo const& other ) const;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // This is just here to avoid compiler warnings with macro constants and boolean literals
+ inline bool isTrue( bool value ){ return value; }
+ inline bool alwaysTrue() { return true; }
+ inline bool alwaysFalse() { return false; }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+ void seedRng( IConfig const& config );
+ unsigned int rngSeed();
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() {
+ return std::string();
+ }
+ };
+ template<typename T>
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+namespace Catch {
+
+ class NotImplementedException : public std::exception
+ {
+ public:
+ NotImplementedException( SourceLineInfo const& lineInfo );
+ NotImplementedException( NotImplementedException const& ) {}
+
+ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+ virtual const char* what() const CATCH_NOEXCEPT;
+
+ private:
+ std::string m_what;
+ SourceLineInfo m_lineInfo;
+ };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct IGeneratorInfo {
+ virtual ~IGeneratorInfo();
+ virtual bool moveNext() = 0;
+ virtual std::size_t getCurrentIndex() const = 0;
+ };
+
+ struct IGeneratorsForTest {
+ virtual ~IGeneratorsForTest();
+
+ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+ virtual bool moveNext() = 0;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ // An intrusive reference counting smart pointer.
+ // T must implement addRef() and release() methods
+ // typically implementing the IShared interface
+ template<typename T>
+ class Ptr {
+ public:
+ Ptr() : m_p( CATCH_NULL ){}
+ Ptr( T* p ) : m_p( p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ Ptr( Ptr const& other ) : m_p( other.m_p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ ~Ptr(){
+ if( m_p )
+ m_p->release();
+ }
+ void reset() {
+ if( m_p )
+ m_p->release();
+ m_p = CATCH_NULL;
+ }
+ Ptr& operator = ( T* p ){
+ Ptr temp( p );
+ swap( temp );
+ return *this;
+ }
+ Ptr& operator = ( Ptr const& other ){
+ Ptr temp( other );
+ swap( temp );
+ return *this;
+ }
+ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+ T* get() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == CATCH_NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
+
+ private:
+ T* m_p;
+ };
+
+ struct IShared : NonCopyable {
+ virtual ~IShared();
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ template<typename T = IShared>
+ struct SharedImpl : T {
+
+ SharedImpl() : m_rc( 0 ){}
+
+ virtual void addRef() const {
+ ++m_rc;
+ }
+ virtual void release() const {
+ if( --m_rc == 0 )
+ delete this;
+ }
+
+ mutable unsigned int m_rc;
+ };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestCase;
+ class Stream;
+ struct IResultCapture;
+ struct IRunner;
+ struct IGeneratorsForTest;
+ struct IConfig;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+ virtual bool advanceGeneratorsForCurrentTest() = 0;
+ virtual Ptr<IConfig const> getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+ };
+
+ IContext& getCurrentContext();
+ IMutableContext& getCurrentMutableContext();
+ void cleanUpContext();
+ Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestCase : IShared {
+ virtual void invoke () const = 0;
+ protected:
+ virtual ~ITestCase();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector<TestCase> const& getAllTests() const = 0;
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+ virtual void invoke() const {
+ C obj;
+ (obj.*m_method)();
+ }
+
+private:
+ virtual ~MethodTestCase() {}
+
+ void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+ NameAndDesc( const char* _name = "", const char* _description= "" )
+ : name( _name ), description( _description )
+ {}
+
+ const char* name;
+ const char* description;
+};
+
+void registerTestCase
+ ( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+struct AutoReg {
+
+ AutoReg
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+ template<typename C>
+ AutoReg
+ ( void (C::*method)(),
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ registerTestCase
+ ( new MethodTestCase<C>( method ),
+ className,
+ nameAndDesc,
+ lineInfo );
+ }
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+void registerTestCaseFunction
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ \
+ struct TestName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ \
+ struct TestCaseName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ void TestCaseName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ inline bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ inline bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+ }
+
+ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct DecomposedExpression
+ {
+ virtual ~DecomposedExpression() {}
+ virtual bool isBinaryExpression() const {
+ return false;
+ }
+ virtual void reconstructExpression( std::string& dest ) const = 0;
+
+ // Only simple binary comparisons can be decomposed.
+ // If more complex check is required then wrap sub-expressions in parentheses.
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+ private:
+ DecomposedExpression& operator = (DecomposedExpression const&);
+ };
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ std::string capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : decomposedExpression( CATCH_NULL )
+ , resultType( ResultWas::Unknown )
+ , negated( false )
+ , parenthesized( false ) {}
+
+ void negate( bool parenthesize ) {
+ negated = !negated;
+ parenthesized = parenthesize;
+ if( resultType == ResultWas::Ok )
+ resultType = ResultWas::ExpressionFailed;
+ else if( resultType == ResultWas::ExpressionFailed )
+ resultType = ResultWas::Ok;
+ }
+
+ std::string const& reconstructExpression() const {
+ if( decomposedExpression != CATCH_NULL ) {
+ decomposedExpression->reconstructExpression( reconstructedExpression );
+ if( parenthesized ) {
+ reconstructedExpression.insert( 0, 1, '(' );
+ reconstructedExpression.append( 1, ')' );
+ }
+ if( negated ) {
+ reconstructedExpression.insert( 0, 1, '!' );
+ }
+ decomposedExpression = CATCH_NULL;
+ }
+ return reconstructedExpression;
+ }
+
+ mutable DecomposedExpression const* decomposedExpression;
+ mutable std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ bool negated;
+ bool parenthesized;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult();
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ ~AssertionResult();
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ AssertionResult( AssertionResult const& ) = default;
+ AssertionResult( AssertionResult && ) = default;
+ AssertionResult& operator = ( AssertionResult const& ) = default;
+ AssertionResult& operator = ( AssertionResult && ) = default;
+# endif
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ std::string getTestMacroName() const;
+ void discardDecomposedExpression() const;
+ void expandDecomposedExpression() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ArgT> struct MatchAllOf;
+ template<typename ArgT> struct MatchAnyOf;
+ template<typename ArgT> struct MatchNotOf;
+
+ class MatcherUntypedBase {
+ public:
+ std::string toString() const {
+ if( m_cachedToString.empty() )
+ m_cachedToString = describe();
+ return m_cachedToString;
+ }
+
+ protected:
+ virtual ~MatcherUntypedBase();
+ virtual std::string describe() const = 0;
+ mutable std::string m_cachedToString;
+ private:
+ MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
+ };
+
+ template<typename ObjectT>
+ struct MatcherMethod {
+ virtual bool match( ObjectT const& arg ) const = 0;
+ };
+ template<typename PtrT>
+ struct MatcherMethod<PtrT*> {
+ virtual bool match( PtrT* arg ) const = 0;
+ };
+
+ template<typename ObjectT, typename ComparatorT = ObjectT>
+ struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
+
+ MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+ MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+ MatchNotOf<ComparatorT> operator ! () const;
+ };
+
+ template<typename ArgT>
+ struct MatchAllOf : MatcherBase<ArgT> {
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (!m_matchers[i]->match(arg))
+ return false;
+ }
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ description += " and ";
+ description += m_matchers[i]->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+ template<typename ArgT>
+ struct MatchAnyOf : MatcherBase<ArgT> {
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (m_matchers[i]->match(arg))
+ return true;
+ }
+ return false;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ description += " or ";
+ description += m_matchers[i]->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+
+ template<typename ArgT>
+ struct MatchNotOf : MatcherBase<ArgT> {
+
+ MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ return !m_underlyingMatcher.match( arg );
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "not " + m_underlyingMatcher.toString();
+ }
+ MatcherBase<ArgT> const& m_underlyingMatcher;
+ };
+
+ template<typename ObjectT, typename ComparatorT>
+ MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+ return MatchAllOf<ComparatorT>() && *this && other;
+ }
+ template<typename ObjectT, typename ComparatorT>
+ MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+ return MatchAnyOf<ComparatorT>() || *this || other;
+ }
+ template<typename ObjectT, typename ComparatorT>
+ MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+ return MatchNotOf<ComparatorT>( *this );
+ }
+
+ } // namespace Impl
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ // - deprecated: prefer ||, && and !
+ template<typename T>
+ inline Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+ return Impl::MatchNotOf<T>( underlyingMatcher );
+ }
+ template<typename T>
+ inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+ return Impl::MatchAllOf<T>() && m1 && m2;
+ }
+ template<typename T>
+ inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+ return Impl::MatchAllOf<T>() && m1 && m2 && m3;
+ }
+ template<typename T>
+ inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+ return Impl::MatchAnyOf<T>() || m1 || m2;
+ }
+ template<typename T>
+ inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+ return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
+ }
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template<typename T> class ExpressionLhs;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str(std::string());
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder : public DecomposedExpression {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition,
+ char const* secondArg = "" );
+ ~ResultBuilder();
+
+ template<typename T>
+ ExpressionLhs<T const&> operator <= ( T const& operand );
+ ExpressionLhs<bool> operator <= ( bool value );
+
+ template<typename T>
+ ResultBuilder& operator << ( T const& value ) {
+ m_stream.oss << value;
+ return *this;
+ }
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+
+ void endExpression( DecomposedExpression const& expr );
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
+ AssertionResult build() const;
+ AssertionResult build( DecomposedExpression const& expr ) const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void captureExpectedException( std::string const& expectedMessage );
+ void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
+ void handleResult( AssertionResult const& result );
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ template<typename ArgT, typename MatcherT>
+ void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
+ void setExceptionGuard();
+ void unsetExceptionGuard();
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+ CopyableStream m_stream;
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ bool m_guardException;
+ };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+ enum Operator {
+ IsEqualTo,
+ IsNotEqualTo,
+ IsLessThan,
+ IsGreaterThan,
+ IsLessThanOrEqualTo,
+ IsGreaterThanOrEqualTo
+ };
+
+ template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
+ template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
+ template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
+ template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
+ template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
+ template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
+ template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+ template<typename T>
+ inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+ // So the compare overloads can be operator agnostic we convey the operator as a template
+ // enum, which is used to specialise an Evaluator for doing the comparison.
+ template<typename T1, typename T2, Operator Op>
+ class Evaluator{};
+
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs) {
+ return bool( opCast( lhs ) == opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsNotEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) != opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) < opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) > opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) >= opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) <= opCast( rhs ) );
+ }
+ };
+
+ template<Operator Op, typename T1, typename T2>
+ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // This level of indirection allows us to specialise for integer types
+ // to avoid signed/ unsigned warnings
+
+ // "base" overload
+ template<Operator Op, typename T1, typename T2>
+ bool compare( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // unsigned X to int
+ template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+
+ // unsigned X to long
+ template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+
+ // int to unsigned X
+ template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+
+ // long to unsigned X
+ template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // pointer to long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+ // pointer to int (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+ // long long to unsigned X
+ template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // unsigned long long to X
+ template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+
+ // pointer to long long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ // pointer to nullptr_t (when comparing against nullptr)
+ template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
+ }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring );
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+ extern const std::string unprintableString;
+
+ #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
+ struct BorgType {
+ template<typename T> BorgType( T const& );
+ };
+
+ struct TrueType { char sizer[1]; };
+ struct FalseType { char sizer[2]; };
+
+ TrueType& testStreamable( std::ostream& );
+ FalseType testStreamable( FalseType );
+
+ FalseType operator<<( std::ostream const&, BorgType const& );
+
+ template<typename T>
+ struct IsStreamInsertable {
+ static std::ostream &s;
+ static T const&t;
+ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+ };
+#else
+ template<typename T>
+ class IsStreamInsertable {
+ template<typename SS, typename TT>
+ static auto test(int)
+ -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
+
+ template<typename, typename>
+ static auto test(...) -> std::false_type;
+
+ public:
+ static const bool value = decltype(test<std::ostream,const T&>(0))::value;
+ };
+#endif
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+ template<typename T,
+ bool IsEnum = std::is_enum<T>::value
+ >
+ struct EnumStringMaker
+ {
+ static std::string convert( T const& ) { return unprintableString; }
+ };
+
+ template<typename T>
+ struct EnumStringMaker<T,true>
+ {
+ static std::string convert( T const& v )
+ {
+ return ::Catch::toString(
+ static_cast<typename std::underlying_type<T>::type>(v)
+ );
+ }
+ };
+#endif
+ template<bool C>
+ struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+ template<typename T>
+ static std::string convert( T const& v )
+ {
+ return EnumStringMaker<T>::convert( v );
+ }
+#else
+ template<typename T>
+ static std::string convert( T const& ) { return unprintableString; }
+#endif
+ };
+
+ template<>
+ struct StringMakerBase<true> {
+ template<typename T>
+ static std::string convert( T const& _value ) {
+ std::ostringstream oss;
+ oss << _value;
+ return oss.str();
+ }
+ };
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template<typename T>
+ inline std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+ Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+ template<typename U>
+ static std::string convert( U* p ) {
+ if( !p )
+ return "NULL";
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+ static std::string convert( R C::* p ) {
+ if( !p )
+ return "NULL";
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+// static std::string convert( std::vector<T,Allocator> const& v ) {
+// return Detail::rangeToString( v.begin(), v.end() );
+// }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+ return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size<Tuple>::value)
+ >
+ struct ElementPrinter {
+ static void print( const Tuple& tuple, std::ostream& os )
+ {
+ os << ( N ? ", " : " " )
+ << Catch::toString(std::get<N>(tuple));
+ ElementPrinter<Tuple,N+1>::print(tuple,os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct ElementPrinter<Tuple,N,false> {
+ static void print( const Tuple&, std::ostream& ) {}
+ };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+ static std::string convert( const std::tuple<Types...>& tuple )
+ {
+ std::ostringstream os;
+ os << '{';
+ TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+ os << " }";
+ return os.str();
+ }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+ template<typename T>
+ std::string makeString( T const& value ) {
+ return StringMaker<T>::convert( value );
+ }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+ return StringMaker<T>::convert( value );
+}
+
+ namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last ) {
+ std::ostringstream oss;
+ oss << "{ ";
+ if( first != last ) {
+ oss << Catch::toString( *first );
+ for( ++first ; first != last ; ++first )
+ oss << ", " << Catch::toString( *first );
+ }
+ oss << " }";
+ return oss.str();
+ }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+
+ ExpressionLhs& operator = ( const ExpressionLhs& );
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+ operator == ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+ operator != ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+ operator < ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThan>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+ operator > ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThan>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+ operator <= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+ operator >= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+ }
+
+ BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ void endExpression() {
+ m_truthy = m_lhs ? true : false;
+ m_rb
+ .setResultType( m_truthy )
+ .endExpression( *this );
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ dest = Catch::toString( m_truthy );
+ }
+
+private:
+ template<Internal::Operator Op, typename RhsT>
+ BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+ return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+ }
+
+ template<Internal::Operator Op>
+ BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+ return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+ bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+ BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+ : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+ BinaryExpression& operator = ( BinaryExpression& );
+
+ void endExpression() const {
+ m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+ .endExpression( *this );
+ }
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string lhs = Catch::toString( m_lhs );
+ std::string rhs = Catch::toString( m_rhs );
+ char delim = lhs.size() + rhs.size() < 40 &&
+ lhs.find('\n') == std::string::npos &&
+ rhs.find('\n') == std::string::npos ? ' ' : '\n';
+ dest.reserve( 7 + lhs.size() + rhs.size() );
+ // 2 for spaces around operator
+ // 2 for operator
+ // 2 for parentheses (conditionally added later)
+ // 1 for negation (conditionally added later)
+ dest = lhs;
+ dest += delim;
+ dest += Internal::OperatorTraits<Op>::getName();
+ dest += delim;
+ dest += rhs;
+ }
+
+private:
+ ResultBuilder& m_rb;
+ LhsT m_lhs;
+ RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+ MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+ : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string matcherAsString = m_matcher.toString();
+ dest = Catch::toString( m_arg );
+ dest += ' ';
+ if( matcherAsString == Detail::unprintableString )
+ dest += m_matcherString;
+ else
+ dest += matcherAsString;
+ }
+
+private:
+ ArgT m_arg;
+ MatcherT m_matcher;
+ char const* m_matcherString;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+ template<typename T>
+ inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
+ return ExpressionLhs<T const&>( *this, operand );
+ }
+
+ inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
+ return ExpressionLhs<bool>( *this, value );
+ }
+
+ template<typename ArgT, typename MatcherT>
+ inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+ char const* matcherString ) {
+ MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+ setResultType( matcher.match( arg ) );
+ endExpression( expr );
+ }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ std::string message;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+ bool operator < ( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageBuilder {
+ MessageBuilder( std::string const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ : m_info( macroName, lineInfo, type )
+ {}
+
+ template<typename T>
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ std::ostringstream m_stream;
+ };
+
+ class ScopedMessage {
+ public:
+ ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage const& other );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct SectionEndInfo;
+ struct MessageInfo;
+ class ScopedMessageBuilder;
+ struct Counts;
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual void assertionEnded( AssertionResult const& result ) = 0;
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+
+ virtual void exceptionEarlyReported() = 0;
+
+ virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+# define CATCH_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+# define CATCH_PLATFORM_IPHONE
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+# define CATCH_PLATFORM_WINDOWS
+# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+# define CATCH_DEFINES_NOMINMAX
+# endif
+# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# endif
+#endif
+
+#include <string>
+
+namespace Catch{
+
+ bool isDebuggerActive();
+ void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ // The following code snippet based on:
+ // http://cocoawithlove.com/2008/03/break-into-debugger.html
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_TRAP() \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" )
+ #else
+ #define CATCH_TRAP() __asm__("int $3\n" : : )
+ #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ // If we can use inline assembler, do it because this allows us to break
+ // directly at the location of the failing check instead of breaking inside
+ // raise() called from it, i.e. one stack frame below.
+ #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+ #define CATCH_TRAP() asm volatile ("int $3")
+ #else // Fall back to the generic way.
+ #include <signal.h>
+
+ #define CATCH_TRAP() raise(SIGTRAP)
+ #endif
+#elif defined(_MSC_VER)
+ #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifdef CATCH_TRAP
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+ #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+ class TestCase;
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+// This can potentially cause false negative, if the test code catches
+// the exception before it propagates back up to the runner.
+#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ __catchResult.setExceptionGuard(); \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ ( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ __catchResult.unsetExceptionGuard(); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+ __catchResult.setExceptionGuard(); \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
+ __catchResult.unsetExceptionGuard(); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+#else
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+ resultBuilder.react();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ ( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureExpectedException( matcher ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( exceptionType ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#else
+ #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << log + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+ try { \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
+ } catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+ struct Counts {
+ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+ Counts operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+ Counts& operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t total() const {
+ return passed + failed + failedButOk;
+ }
+ bool allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+ bool allOk() const {
+ return failed == 0;
+ }
+
+ std::size_t passed;
+ std::size_t failed;
+ std::size_t failedButOk;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+ Totals& operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+#include <string>
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description = std::string() );
+
+ std::string name;
+ std::string description;
+ SourceLineInfo lineInfo;
+ };
+
+ struct SectionEndInfo {
+ SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+ : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+ {}
+
+ SectionInfo sectionInfo;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef _MSC_VER
+
+namespace Catch {
+ typedef unsigned long long UInt64;
+}
+#else
+#include <stdint.h>
+namespace Catch {
+ typedef uint64_t UInt64;
+}
+#endif
+
+namespace Catch {
+ class Timer {
+ public:
+ Timer() : m_ticks( 0 ) {}
+ void start();
+ unsigned int getElapsedMicroseconds() const;
+ unsigned int getElapsedMilliseconds() const;
+ double getElapsedSeconds() const;
+
+ private:
+ UInt64 m_ticks;
+ };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+ class Section : NonCopyable {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ operator bool() const;
+
+ private:
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_SECTION( ... ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+ #define INTERNAL_CATCH_SECTION( name, desc ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+ virtual ~IGenerator() {}
+ virtual T getValue( std::size_t index ) const = 0;
+ virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+ virtual T getValue( std::size_t index ) const {
+ return m_from+static_cast<int>( index );
+ }
+
+ virtual std::size_t size() const {
+ return static_cast<std::size_t>( 1+m_to-m_from );
+ }
+
+private:
+
+ T m_from;
+ T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+ ValuesGenerator(){}
+
+ void add( T value ) {
+ m_values.push_back( value );
+ }
+
+ virtual T getValue( std::size_t index ) const {
+ return m_values[index];
+ }
+
+ virtual std::size_t size() const {
+ return m_values.size();
+ }
+
+private:
+ std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+ CompositeGenerator() : m_totalSize( 0 ) {}
+
+ // *** Move semantics, similar to auto_ptr ***
+ CompositeGenerator( CompositeGenerator& other )
+ : m_fileInfo( other.m_fileInfo ),
+ m_totalSize( 0 )
+ {
+ move( other );
+ }
+
+ CompositeGenerator& setFileInfo( const char* fileInfo ) {
+ m_fileInfo = fileInfo;
+ return *this;
+ }
+
+ ~CompositeGenerator() {
+ deleteAll( m_composed );
+ }
+
+ operator T () const {
+ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+ typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+ typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+ for( size_t index = 0; it != itEnd; ++it )
+ {
+ const IGenerator<T>* generator = *it;
+ if( overallIndex >= index && overallIndex < index + generator->size() )
+ {
+ return generator->getValue( overallIndex-index );
+ }
+ index += generator->size();
+ }
+ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+ }
+
+ void add( const IGenerator<T>* generator ) {
+ m_totalSize += generator->size();
+ m_composed.push_back( generator );
+ }
+
+ CompositeGenerator& then( CompositeGenerator& other ) {
+ move( other );
+ return *this;
+ }
+
+ CompositeGenerator& then( T value ) {
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( value );
+ add( valuesGen );
+ return *this;
+ }
+
+private:
+
+ void move( CompositeGenerator& other ) {
+ m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() );
+ m_totalSize += other.m_totalSize;
+ other.m_composed.clear();
+ }
+
+ std::vector<const IGenerator<T>*> m_composed;
+ std::string m_fileInfo;
+ size_t m_totalSize;
+};
+
+namespace Generators
+{
+ template<typename T>
+ CompositeGenerator<T> between( T from, T to ) {
+ CompositeGenerator<T> generators;
+ generators.add( new BetweenGenerator<T>( from, to ) );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3 ){
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ valuesGen->add( val4 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+#include <vector>
+
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ struct ITestCaseRegistry;
+ struct IExceptionTranslatorRegistry;
+ struct IExceptionTranslator;
+ struct IReporterRegistry;
+ struct IReporterFactory;
+ struct ITagAliasRegistry;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+ virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+ };
+
+ IRegistryHub& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+namespace Catch {
+
+ typedef std::string(*exceptionTranslateFunction)();
+
+ struct IExceptionTranslator;
+ typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+ };
+
+ struct IExceptionTranslatorRegistry {
+ virtual ~IExceptionTranslatorRegistry();
+
+ virtual std::string translateActiveException() const = 0;
+ };
+
+ class ExceptionTranslatorRegistrar {
+ template<typename T>
+ class ExceptionTranslator : public IExceptionTranslator {
+ public:
+
+ ExceptionTranslator( std::string(*translateFunction)( T& ) )
+ : m_translateFunction( translateFunction )
+ {}
+
+ virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+ try {
+ if( it == itEnd )
+ throw;
+ else
+ return (*it)->translate( it+1, itEnd );
+ }
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
+ }
+
+ protected:
+ std::string(*m_translateFunction)( T& );
+ };
+
+ public:
+ template<typename T>
+ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+ getMutableRegistryHub().registerTranslator
+ ( new ExceptionTranslator<T>( translateFunction ) );
+ }
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+ static std::string translatorName( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+ static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ public:
+ explicit Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_margin( 0.0 ),
+ m_scale( 1.0 ),
+ m_value( value )
+ {}
+
+ Approx( Approx const& other )
+ : m_epsilon( other.m_epsilon ),
+ m_margin( other.m_margin ),
+ m_scale( other.m_scale ),
+ m_value( other.m_value )
+ {}
+
+ static Approx custom() {
+ return Approx( 0 );
+ }
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx operator()( T value ) {
+ Approx approx( static_cast<double>(value) );
+ approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ explicit Approx( T value ): Approx(static_cast<double>(value))
+ {}
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( const T& lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ auto lhs_v = double(lhs);
+ bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( Approx const& lhs, const T& rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( T lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( Approx const& lhs, T rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( T lhs, Approx const& rhs ) {
+ return double(lhs) < rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( Approx const& lhs, T rhs ) {
+ return lhs.m_value < double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( T lhs, Approx const& rhs ) {
+ return double(lhs) > rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( Approx const& lhs, T rhs ) {
+ return lhs.m_value > double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& epsilon( T newEpsilon ) {
+ m_epsilon = double(newEpsilon);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& margin( T newMargin ) {
+ m_margin = double(newMargin);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& scale( T newScale ) {
+ m_scale = double(newScale);
+ return *this;
+ }
+
+#else
+
+ Approx operator()( double value ) {
+ Approx approx( value );
+ approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ friend bool operator == ( double lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
+ }
+
+ friend bool operator == ( Approx const& lhs, double rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ friend bool operator != ( double lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ friend bool operator != ( Approx const& lhs, double rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ friend bool operator <= ( double lhs, Approx const& rhs ) {
+ return lhs < rhs.m_value || lhs == rhs;
+ }
+
+ friend bool operator <= ( Approx const& lhs, double rhs ) {
+ return lhs.m_value < rhs || lhs == rhs;
+ }
+
+ friend bool operator >= ( double lhs, Approx const& rhs ) {
+ return lhs > rhs.m_value || lhs == rhs;
+ }
+
+ friend bool operator >= ( Approx const& lhs, double rhs ) {
+ return lhs.m_value > rhs || lhs == rhs;
+ }
+
+ Approx& epsilon( double newEpsilon ) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& margin( double newMargin ) {
+ m_margin = newMargin;
+ return *this;
+ }
+
+ Approx& scale( double newScale ) {
+ m_scale = newScale;
+ return *this;
+ }
+#endif
+
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << "Approx( " << Catch::toString( m_value ) << " )";
+ return oss.str();
+ }
+
+ private:
+ double m_epsilon;
+ double m_margin;
+ double m_scale;
+ double m_value;
+ };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+ return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ struct CasedString
+ {
+ CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+ std::string adjustString( std::string const& str ) const;
+ std::string caseSensitivitySuffix() const;
+
+ CaseSensitive::Choice m_caseSensitivity;
+ std::string m_str;
+ };
+
+ struct StringMatcherBase : MatcherBase<std::string> {
+ StringMatcherBase( std::string const& operation, CasedString const& comparator );
+ virtual std::string describe() const CATCH_OVERRIDE;
+
+ CasedString m_comparator;
+ std::string m_operation;
+ };
+
+ struct EqualsMatcher : StringMatcherBase {
+ EqualsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct ContainsMatcher : StringMatcherBase {
+ ContainsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct StartsWithMatcher : StringMatcherBase {
+ StartsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct EndsWithMatcher : StringMatcherBase {
+ EndsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+
+ } // namespace StdString
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace Vector {
+
+ template<typename T>
+ struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+
+ ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ return std::find(v.begin(), v.end(), m_comparator) != v.end();
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ T const& m_comparator;
+ };
+
+ template<typename T>
+ struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+ ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ // !TBD: see note in EqualsMatcher
+ if (m_comparator.size() > v.size())
+ return false;
+ for (size_t i = 0; i < m_comparator.size(); ++i)
+ if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ std::vector<T> const& m_comparator;
+ };
+
+ template<typename T>
+ struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+ EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ // !TBD: This currently works if all elements can be compared using !=
+ // - a more general approach would be via a compare template that defaults
+ // to using !=. but could be specialised for, e.g. std::vector<T> etc
+ // - then just call that directly
+ if (m_comparator.size() != v.size())
+ return false;
+ for (size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != v[i])
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Equals: " + Catch::toString( m_comparator );
+ }
+ std::vector<T> const& m_comparator;
+ };
+
+ } // namespace Vector
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ template<typename T>
+ Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+ return Vector::ContainsMatcher<T>( comparator );
+ }
+
+ template<typename T>
+ Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+ return Vector::ContainsElementMatcher<T>( comparator );
+ }
+
+ template<typename T>
+ Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+ return Vector::EqualsMatcher<T>( comparator );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias {
+ TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+ std::string tag;
+ SourceLineInfo lineInfo;
+ };
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( CATCH_NULL ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
+ {}
+
+ ~Option() {
+ reset();
+ }
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = CATCH_NULL;
+ }
+
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != CATCH_NULL; }
+ bool none() const { return nullableValue == CATCH_NULL; }
+
+ bool operator !() const { return nullableValue == CATCH_NULL; }
+ operator SafeBool::type() const {
+ return SafeBool::makeSafe( some() );
+ }
+
+ private:
+ T *nullableValue;
+ union {
+ char storage[sizeof(T)];
+
+ // These are here to force alignment for the storage
+ long double dummy1;
+ void (*dummy2)();
+ long double dummy3;
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+ long long dummy4;
+#endif
+ };
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct ITagAliasRegistry {
+ virtual ~ITagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+ static ITagAliasRegistry const& get();
+ };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ struct ITestCase;
+
+ struct TestCaseInfo {
+ enum SpecialProperties{
+ None = 0,
+ IsHidden = 1 << 1,
+ ShouldFail = 1 << 2,
+ MayFail = 1 << 3,
+ Throws = 1 << 4,
+ NonPortable = 1 << 5
+ };
+
+ TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo );
+
+ TestCaseInfo( TestCaseInfo const& other );
+
+ friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
+ bool isHidden() const;
+ bool throws() const;
+ bool okToFail() const;
+ bool expectedToFail() const;
+
+ std::string name;
+ std::string className;
+ std::string description;
+ std::set<std::string> tags;
+ std::set<std::string> lcaseTags;
+ std::string tagsAsString;
+ SourceLineInfo lineInfo;
+ SpecialProperties properties;
+ };
+
+ class TestCase : public TestCaseInfo {
+ public:
+
+ TestCase( ITestCase* testCase, TestCaseInfo const& info );
+ TestCase( TestCase const& other );
+
+ TestCase withName( std::string const& _newName ) const;
+
+ void invoke() const;
+
+ TestCaseInfo const& getTestCaseInfo() const;
+
+ void swap( TestCase& other );
+ bool operator == ( TestCase const& other ) const;
+ bool operator < ( TestCase const& other ) const;
+ TestCase& operator = ( TestCase const& other );
+
+ private:
+ Ptr<ITestCase> test;
+ };
+
+ TestCase makeTestCase( ITestCase* testCase,
+ std::string const& className,
+ std::string const& name,
+ std::string const& description,
+ SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+ class OcMethod : public SharedImpl<ITestCase> {
+
+ public:
+ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+ virtual void invoke() const {
+ id obj = [[m_cls alloc] init];
+
+ performOptionalSelector( obj, @selector(setUp) );
+ performOptionalSelector( obj, m_sel );
+ performOptionalSelector( obj, @selector(tearDown) );
+
+ arcSafeRelease( obj );
+ }
+ private:
+ virtual ~OcMethod() {}
+
+ Class m_cls;
+ SEL m_sel;
+ };
+
+ namespace Detail{
+
+ inline std::string getAnnotation( Class cls,
+ std::string const& annotationName,
+ std::string const& testCaseName ) {
+ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+ SEL sel = NSSelectorFromString( selStr );
+ arcSafeRelease( selStr );
+ id value = performOptionalSelector( cls, sel );
+ if( value )
+ return [(NSString*)value UTF8String];
+ return "";
+ }
+ }
+
+ inline size_t registerTestMethods() {
+ size_t noTestMethods = 0;
+ int noClasses = objc_getClassList( CATCH_NULL, 0 );
+
+ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+ objc_getClassList( classes, noClasses );
+
+ for( int c = 0; c < noClasses; c++ ) {
+ Class cls = classes[c];
+ {
+ u_int count;
+ Method* methods = class_copyMethodList( cls, &count );
+ for( u_int m = 0; m < count ; m++ ) {
+ SEL selector = method_getName(methods[m]);
+ std::string methodName = sel_getName(selector);
+ if( startsWith( methodName, "Catch_TestCase_" ) ) {
+ std::string testCaseName = methodName.substr( 15 );
+ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+ const char* className = class_getName( cls );
+
+ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+ noTestMethods++;
+ }
+ }
+ free(methods);
+ }
+ }
+ return noTestMethods;
+ }
+
+ namespace Matchers {
+ namespace Impl {
+ namespace NSStringMatchers {
+
+ struct StringHolder : MatcherBase<NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ virtual bool match( NSString* arg ) const CATCH_OVERRIDE {
+ return false;
+ }
+
+ NSString* m_substr;
+ };
+
+ struct Equals : StringHolder {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const CATCH_OVERRIDE {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "equals string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "contains string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "starts with: " + Catch::toString( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "ends with: " + Catch::toString( m_substr );
+ }
+ };
+
+ } // namespace NSStringMatchers
+ } // namespace Impl
+
+ inline Impl::NSStringMatchers::Equals
+ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+ inline Impl::NSStringMatchers::Contains
+ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+ inline Impl::NSStringMatchers::StartsWith
+ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+ inline Impl::NSStringMatchers::EndsWith
+ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+ } // namespace Matchers
+
+ using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_IMPL
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+class LeakDetector {
+public:
+ LeakDetector() {
+ int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flag |= _CRTDBG_LEAK_CHECK_DF;
+ flag |= _CRTDBG_ALLOC_MEM_DF;
+ _CrtSetDbgFlag(flag);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ // Change this to leaking allocation's number to break there
+ _CrtSetBreakAlloc(-1);
+ }
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_session.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+#include <stdexcept>
+
+namespace Catch
+{
+ class WildcardPattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+
+ WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_wildcard( NoWildcard ),
+ m_pattern( adjustCase( pattern ) )
+ {
+ if( startsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+ virtual ~WildcardPattern();
+ virtual bool matches( std::string const& str ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_pattern == adjustCase( str );
+ case WildcardAtStart:
+ return endsWith( adjustCase( str ), m_pattern );
+ case WildcardAtEnd:
+ return startsWith( adjustCase( str ), m_pattern );
+ case WildcardAtBothEnds:
+ return contains( adjustCase( str ), m_pattern );
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+ throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ }
+ private:
+ std::string adjustCase( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+ }
+ CaseSensitive::Choice m_caseSensitivity;
+ WildcardPosition m_wildcard;
+ std::string m_pattern;
+ };
+}
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec {
+ struct Pattern : SharedImpl<> {
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ };
+ class NamePattern : public Pattern {
+ public:
+ NamePattern( std::string const& name )
+ : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
+ virtual ~NamePattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return m_wildcardPattern.matches( toLower( testCase.name ) );
+ }
+ private:
+ WildcardPattern m_wildcardPattern;
+ };
+
+ class TagPattern : public Pattern {
+ public:
+ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ virtual ~TagPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+ }
+ private:
+ std::string m_tag;
+ };
+
+ class ExcludedPattern : public Pattern {
+ public:
+ ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+ virtual ~ExcludedPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ private:
+ Ptr<Pattern> m_underlyingPattern;
+ };
+
+ struct Filter {
+ std::vector<Ptr<Pattern> > m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const {
+ // All patterns in a filter must match for the filter to be a match
+ for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) {
+ if( !(*it)->matches( testCase ) )
+ return false;
+ }
+ return true;
+ }
+ };
+
+ public:
+ bool hasFilters() const {
+ return !m_filters.empty();
+ }
+ bool matches( TestCaseInfo const& testCase ) const {
+ // A TestSpec matches if any filter matches
+ for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+ if( it->matches( testCase ) )
+ return true;
+ return false;
+ }
+
+ private:
+ std::vector<Filter> m_filters;
+
+ friend class TestSpecParser;
+ };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+ Mode m_mode;
+ bool m_exclusion;
+ std::size_t m_start, m_pos;
+ std::string m_arg;
+ std::vector<std::size_t> m_escapeChars;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+ ITagAliasRegistry const* m_tagAliases;
+
+ public:
+ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+ TestSpecParser& parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+ m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ m_escapeChars.clear();
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+ visitChar( m_arg[m_pos] );
+ if( m_mode == Name )
+ addPattern<TestSpec::NamePattern>();
+ return *this;
+ }
+ TestSpec testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
+ private:
+ void visitChar( char c ) {
+ if( m_mode == None ) {
+ switch( c ) {
+ case ' ': return;
+ case '~': m_exclusion = true; return;
+ case '[': return startNewMode( Tag, ++m_pos );
+ case '"': return startNewMode( QuotedName, ++m_pos );
+ case '\\': return escape();
+ default: startNewMode( Name, m_pos ); break;
+ }
+ }
+ if( m_mode == Name ) {
+ if( c == ',' ) {
+ addPattern<TestSpec::NamePattern>();
+ addFilter();
+ }
+ else if( c == '[' ) {
+ if( subString() == "exclude:" )
+ m_exclusion = true;
+ else
+ addPattern<TestSpec::NamePattern>();
+ startNewMode( Tag, ++m_pos );
+ }
+ else if( c == '\\' )
+ escape();
+ }
+ else if( m_mode == EscapedName )
+ m_mode = Name;
+ else if( m_mode == QuotedName && c == '"' )
+ addPattern<TestSpec::NamePattern>();
+ else if( m_mode == Tag && c == ']' )
+ addPattern<TestSpec::TagPattern>();
+ }
+ void startNewMode( Mode mode, std::size_t start ) {
+ m_mode = mode;
+ m_start = start;
+ }
+ void escape() {
+ if( m_mode == None )
+ m_start = m_pos;
+ m_mode = EscapedName;
+ m_escapeChars.push_back( m_pos );
+ }
+ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ template<typename T>
+ void addPattern() {
+ std::string token = subString();
+ for( size_t i = 0; i < m_escapeChars.size(); ++i )
+ token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+ m_escapeChars.clear();
+ if( startsWith( token, "exclude:" ) ) {
+ m_exclusion = true;
+ token = token.substr( 8 );
+ }
+ if( !token.empty() ) {
+ Ptr<TestSpec::Pattern> pattern = new T( token );
+ if( m_exclusion )
+ pattern = new TestSpec::ExcludedPattern( pattern );
+ m_currentFilter.m_patterns.push_back( pattern );
+ }
+ m_exclusion = false;
+ m_mode = None;
+ }
+ void addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+ m_testSpec.m_filters.push_back( m_currentFilter );
+ m_currentFilter = TestSpec::Filter();
+ }
+ }
+ };
+ inline TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct Verbosity { enum Level {
+ NoOutput = 0,
+ Quiet,
+ Normal
+ }; };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+ struct RunTests { enum InWhatOrder {
+ InDeclarationOrder,
+ InLexicographicalOrder,
+ InRandomOrder
+ }; };
+ struct UseColour { enum YesOrNo {
+ Auto,
+ Yes,
+ No
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : IShared {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ virtual RunTests::InWhatOrder runOrder() const = 0;
+ virtual unsigned int rngSeed() const = 0;
+ virtual UseColour::YesOrNo useColour() const = 0;
+ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+
+ };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+ class StreamBufBase : public std::streambuf {
+ public:
+ virtual ~StreamBufBase() CATCH_NOEXCEPT;
+ };
+}
+
+#include <streambuf>
+#include <ostream>
+#include <fstream>
+#include <memory>
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+
+ struct IStream {
+ virtual ~IStream() CATCH_NOEXCEPT;
+ virtual std::ostream& stream() const = 0;
+ };
+
+ class FileStream : public IStream {
+ mutable std::ofstream m_ofs;
+ public:
+ FileStream( std::string const& filename );
+ virtual ~FileStream() CATCH_NOEXCEPT;
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+
+ class CoutStream : public IStream {
+ mutable std::ostream m_os;
+ public:
+ CoutStream();
+ virtual ~CoutStream() CATCH_NOEXCEPT;
+
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+
+ class DebugOutStream : public IStream {
+ CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
+ mutable std::ostream m_os;
+ public:
+ DebugOutStream();
+ virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <stdexcept>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+ struct ConfigData {
+
+ ConfigData()
+ : listTests( false ),
+ listTags( false ),
+ listReporters( false ),
+ listTestNamesOnly( false ),
+ showSuccessfulTests( false ),
+ shouldDebugBreak( false ),
+ noThrow( false ),
+ showHelp( false ),
+ showInvisibles( false ),
+ filenamesAsTags( false ),
+ abortAfter( -1 ),
+ rngSeed( 0 ),
+ verbosity( Verbosity::Normal ),
+ warnings( WarnAbout::Nothing ),
+ showDurations( ShowDurations::DefaultForReporter ),
+ runOrder( RunTests::InDeclarationOrder ),
+ useColour( UseColour::Auto )
+ {}
+
+ bool listTests;
+ bool listTags;
+ bool listReporters;
+ bool listTestNamesOnly;
+
+ bool showSuccessfulTests;
+ bool shouldDebugBreak;
+ bool noThrow;
+ bool showHelp;
+ bool showInvisibles;
+ bool filenamesAsTags;
+
+ int abortAfter;
+ unsigned int rngSeed;
+
+ Verbosity::Level verbosity;
+ WarnAbout::What warnings;
+ ShowDurations::OrNot showDurations;
+ RunTests::InWhatOrder runOrder;
+ UseColour::YesOrNo useColour;
+
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+
+ std::vector<std::string> reporterNames;
+ std::vector<std::string> testsOrTags;
+ std::vector<std::string> sectionsToRun;
+ };
+
+ class Config : public SharedImpl<IConfig> {
+ private:
+ Config( Config const& other );
+ Config& operator = ( Config const& other );
+ virtual void dummy();
+ public:
+
+ Config()
+ {}
+
+ Config( ConfigData const& data )
+ : m_data( data ),
+ m_stream( openStream() )
+ {
+ if( !data.testsOrTags.empty() ) {
+ TestSpecParser parser( ITagAliasRegistry::get() );
+ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+ parser.parse( data.testsOrTags[i] );
+ m_testSpec = parser.testSpec();
+ }
+ }
+
+ virtual ~Config() {}
+
+ std::string const& getFilename() const {
+ return m_data.outputFilename ;
+ }
+
+ bool listTests() const { return m_data.listTests; }
+ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+ bool listTags() const { return m_data.listTags; }
+ bool listReporters() const { return m_data.listReporters; }
+
+ std::string getProcessName() const { return m_data.processName; }
+
+ std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
+ std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
+
+ virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
+
+ bool showHelp() const { return m_data.showHelp; }
+
+ // IConfig interface
+ virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; }
+ virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); }
+ virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
+ virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; }
+ virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; }
+ virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; }
+ virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
+ virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
+ virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
+
+ private:
+
+ IStream const* openStream() {
+ if( m_data.outputFilename.empty() )
+ return new CoutStream();
+ else if( m_data.outputFilename[0] == '%' ) {
+ if( m_data.outputFilename == "%debug" )
+ return new DebugOutStream();
+ else
+ throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+ }
+ else
+ return new FileStream( m_data.outputFilename );
+ }
+ ConfigData m_data;
+
+ CATCH_AUTO_PTR( IStream const ) m_stream;
+ TestSpec m_testSpec;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Version 0.0.2.4
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include <cctype>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+# define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+# define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+# define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
+#include <map>
+#include <stdexcept>
+#include <memory>
+
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+ struct UnpositionalTag {};
+
+ extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+ UnpositionalTag _;
+#endif
+
+ namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ using namespace Tbc;
+
+ inline bool startsWith( std::string const& str, std::string const& prefix ) {
+ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+ }
+
+ template<typename T> struct RemoveConstRef{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+ template<typename T> struct IsBool { static const bool value = false; };
+ template<> struct IsBool<bool> { static const bool value = true; };
+
+ template<typename T>
+ void convertInto( std::string const& _source, T& _dest ) {
+ std::stringstream ss;
+ ss << _source;
+ ss >> _dest;
+ if( ss.fail() )
+ throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+ }
+ inline void convertInto( std::string const& _source, std::string& _dest ) {
+ _dest = _source;
+ }
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( c ) );
+ }
+ inline void convertInto( std::string const& _source, bool& _dest ) {
+ std::string sourceLC = _source;
+ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
+ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+ _dest = true;
+ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+ _dest = false;
+ else
+ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
+ }
+
+ template<typename ConfigT>
+ struct IArgFunction {
+ virtual ~IArgFunction() {}
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
+ IArgFunction() = default;
+ IArgFunction( IArgFunction const& ) = default;
+#endif
+ virtual void set( ConfigT& config, std::string const& value ) const = 0;
+ virtual bool takesArg() const = 0;
+ virtual IArgFunction* clone() const = 0;
+ };
+
+ template<typename ConfigT>
+ class BoundArgFunction {
+ public:
+ BoundArgFunction() : functionObj( CLARA_NULL ) {}
+ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
+ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
+ delete functionObj;
+ functionObj = newFunctionObj;
+ return *this;
+ }
+ ~BoundArgFunction() { delete functionObj; }
+
+ void set( ConfigT& config, std::string const& value ) const {
+ functionObj->set( config, value );
+ }
+ bool takesArg() const { return functionObj->takesArg(); }
+
+ bool isSet() const {
+ return functionObj != CLARA_NULL;
+ }
+ private:
+ IArgFunction<ConfigT>* functionObj;
+ };
+
+ template<typename C>
+ struct NullBinder : IArgFunction<C>{
+ virtual void set( C&, std::string const& ) const {}
+ virtual bool takesArg() const { return true; }
+ virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+ };
+
+ template<typename C, typename M>
+ struct BoundDataMember : IArgFunction<C>{
+ BoundDataMember( M C::* _member ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ convertInto( stringValue, p.*member );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+ M C::* member;
+ };
+ template<typename C, typename M>
+ struct BoundUnaryMethod : IArgFunction<C>{
+ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( stringValue, value );
+ (p.*member)( value );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+ void (C::*member)( M );
+ };
+ template<typename C>
+ struct BoundNullaryMethod : IArgFunction<C>{
+ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ (p.*member)();
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+ void (C::*member)();
+ };
+
+ template<typename C>
+ struct BoundUnaryFunction : IArgFunction<C>{
+ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ function( obj );
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+ void (*function)( C& );
+ };
+
+ template<typename C, typename T>
+ struct BoundBinaryFunction : IArgFunction<C>{
+ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( stringValue, value );
+ function( obj, value );
+ }
+ virtual bool takesArg() const { return !IsBool<T>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+ void (*function)( C&, T );
+ };
+
+ } // namespace Detail
+
+ inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+ std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+ for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+ args[i] = argv[i];
+
+ return args;
+ }
+
+ class Parser {
+ enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+ Mode mode;
+ std::size_t from;
+ bool inQuotes;
+ public:
+
+ struct Token {
+ enum Type { Positional, ShortOpt, LongOpt };
+ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+ Type type;
+ std::string data;
+ };
+
+ Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+ void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
+ const std::string doubleDash = "--";
+ for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+ parseIntoTokens( args[i], tokens);
+ }
+
+ void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+ for( std::size_t i = 0; i < arg.size(); ++i ) {
+ char c = arg[i];
+ if( c == '"' )
+ inQuotes = !inQuotes;
+ mode = handleMode( i, c, arg, tokens );
+ }
+ mode = handleMode( arg.size(), '\0', arg, tokens );
+ }
+ Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ switch( mode ) {
+ case None: return handleNone( i, c );
+ case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+ case ShortOpt:
+ case LongOpt:
+ case SlashOpt: return handleOpt( i, c, arg, tokens );
+ case Positional: return handlePositional( i, c, arg, tokens );
+ default: throw std::logic_error( "Unknown mode" );
+ }
+ }
+
+ Mode handleNone( std::size_t i, char c ) {
+ if( inQuotes ) {
+ from = i;
+ return Positional;
+ }
+ switch( c ) {
+ case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+ case '/': from = i+1; return SlashOpt;
+#endif
+ default: from = i; return Positional;
+ }
+ }
+ Mode handleMaybeShortOpt( std::size_t i, char c ) {
+ switch( c ) {
+ case '-': from = i+1; return LongOpt;
+ default: from = i; return ShortOpt;
+ }
+ }
+
+ Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string optName = arg.substr( from, i-from );
+ if( mode == ShortOpt )
+ for( std::size_t j = 0; j < optName.size(); ++j )
+ tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+ else if( mode == SlashOpt && optName.size() == 1 )
+ tokens.push_back( Token( Token::ShortOpt, optName ) );
+ else
+ tokens.push_back( Token( Token::LongOpt, optName ) );
+ return None;
+ }
+ Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string data = arg.substr( from, i-from );
+ tokens.push_back( Token( Token::Positional, data ) );
+ return None;
+ }
+ };
+
+ template<typename ConfigT>
+ struct CommonArgProperties {
+ CommonArgProperties() {}
+ CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+ Detail::BoundArgFunction<ConfigT> boundField;
+ std::string description;
+ std::string detail;
+ std::string placeholder; // Only value if boundField takes an arg
+
+ bool takesArg() const {
+ return !placeholder.empty();
+ }
+ void validate() const {
+ if( !boundField.isSet() )
+ throw std::logic_error( "option not bound" );
+ }
+ };
+ struct OptionArgProperties {
+ std::vector<std::string> shortNames;
+ std::string longName;
+
+ bool hasShortName( std::string const& shortName ) const {
+ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+ }
+ bool hasLongName( std::string const& _longName ) const {
+ return _longName == longName;
+ }
+ };
+ struct PositionalArgProperties {
+ PositionalArgProperties() : position( -1 ) {}
+ int position; // -1 means non-positional (floating)
+
+ bool isFixedPositional() const {
+ return position != -1;
+ }
+ };
+
+ template<typename ConfigT>
+ class CommandLine {
+
+ struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+ Arg() {}
+ Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+ using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+ std::string dbgName() const {
+ if( !longName.empty() )
+ return "--" + longName;
+ if( !shortNames.empty() )
+ return "-" + shortNames[0];
+ return "positional args";
+ }
+ std::string commands() const {
+ std::ostringstream oss;
+ bool first = true;
+ std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+ for(; it != itEnd; ++it ) {
+ if( first )
+ first = false;
+ else
+ oss << ", ";
+ oss << "-" << *it;
+ }
+ if( !longName.empty() ) {
+ if( !first )
+ oss << ", ";
+ oss << "--" << longName;
+ }
+ if( !placeholder.empty() )
+ oss << " <" << placeholder << ">";
+ return oss.str();
+ }
+ };
+
+ typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
+
+ friend void addOptName( Arg& arg, std::string const& optName )
+ {
+ if( optName.empty() )
+ return;
+ if( Detail::startsWith( optName, "--" ) ) {
+ if( !arg.longName.empty() )
+ throw std::logic_error( "Only one long opt may be specified. '"
+ + arg.longName
+ + "' already specified, now attempting to add '"
+ + optName + "'" );
+ arg.longName = optName.substr( 2 );
+ }
+ else if( Detail::startsWith( optName, "-" ) )
+ arg.shortNames.push_back( optName.substr( 1 ) );
+ else
+ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+ }
+ friend void setPositionalArg( Arg& arg, int position )
+ {
+ arg.position = position;
+ }
+
+ class ArgBuilder {
+ public:
+ ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+ // Bind a non-boolean data member (requires placeholder string)
+ template<typename C, typename M>
+ void bind( M C::* field, std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+ m_arg->placeholder = placeholder;
+ }
+ // Bind a boolean data member (no placeholder required)
+ template<typename C>
+ void bind( bool C::* field ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+ }
+
+ // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+ template<typename C, typename M>
+ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+ m_arg->placeholder = placeholder;
+ }
+
+ // Bind a method taking a single, boolean argument (no placeholder string required)
+ template<typename C>
+ void bind( void (C::* unaryMethod)( bool ) ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+ }
+
+ // Bind a method that takes no arguments (will be called if opt is present)
+ template<typename C>
+ void bind( void (C::* nullaryMethod)() ) {
+ m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+ template<typename C>
+ void bind( void (* unaryFunction)( C& ) ) {
+ m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+ template<typename C, typename T>
+ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+ m_arg->placeholder = placeholder;
+ }
+
+ ArgBuilder& describe( std::string const& description ) {
+ m_arg->description = description;
+ return *this;
+ }
+ ArgBuilder& detail( std::string const& detail ) {
+ m_arg->detail = detail;
+ return *this;
+ }
+
+ protected:
+ Arg* m_arg;
+ };
+
+ class OptBuilder : public ArgBuilder {
+ public:
+ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+ OptBuilder& operator[]( std::string const& optName ) {
+ addOptName( *ArgBuilder::m_arg, optName );
+ return *this;
+ }
+ };
+
+ public:
+
+ CommandLine()
+ : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+ m_highestSpecifiedArgPosition( 0 ),
+ m_throwOnUnrecognisedTokens( false )
+ {}
+ CommandLine( CommandLine const& other )
+ : m_boundProcessName( other.m_boundProcessName ),
+ m_options ( other.m_options ),
+ m_positionalArgs( other.m_positionalArgs ),
+ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+ {
+ if( other.m_floatingArg.get() )
+ m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
+ }
+
+ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+ m_throwOnUnrecognisedTokens = shouldThrow;
+ return *this;
+ }
+
+ OptBuilder operator[]( std::string const& optName ) {
+ m_options.push_back( Arg() );
+ addOptName( m_options.back(), optName );
+ OptBuilder builder( &m_options.back() );
+ return builder;
+ }
+
+ ArgBuilder operator[]( int position ) {
+ m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+ if( position > m_highestSpecifiedArgPosition )
+ m_highestSpecifiedArgPosition = position;
+ setPositionalArg( m_positionalArgs[position], position );
+ ArgBuilder builder( &m_positionalArgs[position] );
+ return builder;
+ }
+
+ // Invoke this with the _ instance
+ ArgBuilder operator[]( UnpositionalTag ) {
+ if( m_floatingArg.get() )
+ throw std::logic_error( "Only one unpositional argument can be added" );
+ m_floatingArg.reset( new Arg() );
+ ArgBuilder builder( m_floatingArg.get() );
+ return builder;
+ }
+
+ template<typename C, typename M>
+ void bindProcessName( M C::* field ) {
+ m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+ }
+ template<typename C, typename M>
+ void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+ m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+ }
+
+ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+ typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+ std::size_t maxWidth = 0;
+ for( it = itBegin; it != itEnd; ++it )
+ maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+ for( it = itBegin; it != itEnd; ++it ) {
+ Detail::Text usage( it->commands(), Detail::TextAttributes()
+ .setWidth( maxWidth+indent )
+ .setIndent( indent ) );
+ Detail::Text desc( it->description, Detail::TextAttributes()
+ .setWidth( width - maxWidth - 3 ) );
+
+ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+ std::string usageCol = i < usage.size() ? usage[i] : "";
+ os << usageCol;
+
+ if( i < desc.size() && !desc[i].empty() )
+ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+ << desc[i];
+ os << "\n";
+ }
+ }
+ }
+ std::string optUsage() const {
+ std::ostringstream oss;
+ optUsage( oss );
+ return oss.str();
+ }
+
+ void argSynopsis( std::ostream& os ) const {
+ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+ if( i > 1 )
+ os << " ";
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+ if( it != m_positionalArgs.end() )
+ os << "<" << it->second.placeholder << ">";
+ else if( m_floatingArg.get() )
+ os << "<" << m_floatingArg->placeholder << ">";
+ else
+ throw std::logic_error( "non consecutive positional arguments with no floating args" );
+ }
+ // !TBD No indication of mandatory args
+ if( m_floatingArg.get() ) {
+ if( m_highestSpecifiedArgPosition > 1 )
+ os << " ";
+ os << "[<" << m_floatingArg->placeholder << "> ...]";
+ }
+ }
+ std::string argSynopsis() const {
+ std::ostringstream oss;
+ argSynopsis( oss );
+ return oss.str();
+ }
+
+ void usage( std::ostream& os, std::string const& procName ) const {
+ validate();
+ os << "usage:\n " << procName << " ";
+ argSynopsis( os );
+ if( !m_options.empty() ) {
+ os << " [options]\n\nwhere options are: \n";
+ optUsage( os, 2 );
+ }
+ os << "\n";
+ }
+ std::string usage( std::string const& procName ) const {
+ std::ostringstream oss;
+ usage( oss, procName );
+ return oss.str();
+ }
+
+ ConfigT parse( std::vector<std::string> const& args ) const {
+ ConfigT config;
+ parseInto( args, config );
+ return config;
+ }
+
+ std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+ std::string processName = args.empty() ? std::string() : args[0];
+ std::size_t lastSlash = processName.find_last_of( "/\\" );
+ if( lastSlash != std::string::npos )
+ processName = processName.substr( lastSlash+1 );
+ m_boundProcessName.set( config, processName );
+ std::vector<Parser::Token> tokens;
+ Parser parser;
+ parser.parseIntoTokens( args, tokens );
+ return populate( tokens, config );
+ }
+
+ std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ validate();
+ std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+ unusedTokens = populateFixedArgs( unusedTokens, config );
+ unusedTokens = populateFloatingArgs( unusedTokens, config );
+ return unusedTokens;
+ }
+
+ std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ std::vector<std::string> errors;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+ for(; it != itEnd; ++it ) {
+ Arg const& arg = *it;
+
+ try {
+ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+ if( arg.takesArg() ) {
+ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+ errors.push_back( "Expected argument to option: " + token.data );
+ else
+ arg.boundField.set( config, tokens[++i].data );
+ }
+ else {
+ arg.boundField.set( config, "true" );
+ }
+ break;
+ }
+ }
+ catch( std::exception& ex ) {
+ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+ }
+ }
+ if( it == itEnd ) {
+ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+ unusedTokens.push_back( token );
+ else if( errors.empty() && m_throwOnUnrecognisedTokens )
+ errors.push_back( "unrecognised option: " + token.data );
+ }
+ }
+ if( !errors.empty() ) {
+ std::ostringstream oss;
+ for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+ it != itEnd;
+ ++it ) {
+ if( it != errors.begin() )
+ oss << "\n";
+ oss << *it;
+ }
+ throw std::runtime_error( oss.str() );
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ int position = 1;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+ if( it != m_positionalArgs.end() )
+ it->second.boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ if( token.type == Parser::Token::Positional )
+ position++;
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ if( !m_floatingArg.get() )
+ return tokens;
+ std::vector<Parser::Token> unusedTokens;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ if( token.type == Parser::Token::Positional )
+ m_floatingArg->boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ }
+ return unusedTokens;
+ }
+
+ void validate() const
+ {
+ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+ throw std::logic_error( "No options or arguments specified" );
+
+ for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
+ itEnd = m_options.end();
+ it != itEnd; ++it )
+ it->validate();
+ }
+
+ private:
+ Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+ std::vector<Arg> m_options;
+ std::map<int, Arg> m_positionalArgs;
+ ArgAutoPtr m_floatingArg;
+ int m_highestSpecifiedArgPosition;
+ bool m_throwOnUnrecognisedTokens;
+ };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+ inline void abortAfterX( ConfigData& config, int x ) {
+ if( x < 1 )
+ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+ config.abortAfter = x;
+ }
+ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+ inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
+ inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
+
+ inline void addWarning( ConfigData& config, std::string const& _warning ) {
+ if( _warning == "NoAssertions" )
+ config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+ else
+ throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
+ }
+ inline void setOrder( ConfigData& config, std::string const& order ) {
+ if( startsWith( "declared", order ) )
+ config.runOrder = RunTests::InDeclarationOrder;
+ else if( startsWith( "lexical", order ) )
+ config.runOrder = RunTests::InLexicographicalOrder;
+ else if( startsWith( "random", order ) )
+ config.runOrder = RunTests::InRandomOrder;
+ else
+ throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' );
+ }
+ inline void setRngSeed( ConfigData& config, std::string const& seed ) {
+ if( seed == "time" ) {
+ config.rngSeed = static_cast<unsigned int>( std::time(0) );
+ }
+ else {
+ std::stringstream ss;
+ ss << seed;
+ ss >> config.rngSeed;
+ if( ss.fail() )
+ throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
+ }
+ }
+ inline void setVerbosity( ConfigData& config, int level ) {
+ // !TBD: accept strings?
+ config.verbosity = static_cast<Verbosity::Level>( level );
+ }
+ inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+ config.showDurations = _showDurations
+ ? ShowDurations::Always
+ : ShowDurations::Never;
+ }
+ inline void setUseColour( ConfigData& config, std::string const& value ) {
+ std::string mode = toLower( value );
+
+ if( mode == "yes" )
+ config.useColour = UseColour::Yes;
+ else if( mode == "no" )
+ config.useColour = UseColour::No;
+ else if( mode == "auto" )
+ config.useColour = UseColour::Auto;
+ else
+ throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+ }
+ inline void forceColour( ConfigData& config ) {
+ config.useColour = UseColour::Yes;
+ }
+ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+ std::ifstream f( _filename.c_str() );
+ if( !f.is_open() )
+ throw std::domain_error( "Unable to load input file: " + _filename );
+
+ std::string line;
+ while( std::getline( f, line ) ) {
+ line = trim(line);
+ if( !line.empty() && !startsWith( line, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + line + '"';
+ addTestOrTags( config, line + ',' );
+ }
+ }
+ }
+
+ inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+ using namespace Clara;
+ CommandLine<ConfigData> cli;
+
+ cli.bindProcessName( &ConfigData::processName );
+
+ cli["-?"]["-h"]["--help"]
+ .describe( "display usage information" )
+ .bind( &ConfigData::showHelp );
+
+ cli["-l"]["--list-tests"]
+ .describe( "list all/matching test cases" )
+ .bind( &ConfigData::listTests );
+
+ cli["-t"]["--list-tags"]
+ .describe( "list all/matching tags" )
+ .bind( &ConfigData::listTags );
+
+ cli["-s"]["--success"]
+ .describe( "include successful tests in output" )
+ .bind( &ConfigData::showSuccessfulTests );
+
+ cli["-b"]["--break"]
+ .describe( "break into debugger on failure" )
+ .bind( &ConfigData::shouldDebugBreak );
+
+ cli["-e"]["--nothrow"]
+ .describe( "skip exception tests" )
+ .bind( &ConfigData::noThrow );
+
+ cli["-i"]["--invisibles"]
+ .describe( "show invisibles (tabs, newlines)" )
+ .bind( &ConfigData::showInvisibles );
+
+ cli["-o"]["--out"]
+ .describe( "output filename" )
+ .bind( &ConfigData::outputFilename, "filename" );
+
+ cli["-r"]["--reporter"]
+// .placeholder( "name[:filename]" )
+ .describe( "reporter to use (defaults to console)" )
+ .bind( &addReporterName, "name" );
+
+ cli["-n"]["--name"]
+ .describe( "suite name" )
+ .bind( &ConfigData::name, "name" );
+
+ cli["-a"]["--abort"]
+ .describe( "abort at first failure" )
+ .bind( &abortAfterFirst );
+
+ cli["-x"]["--abortx"]
+ .describe( "abort after x failures" )
+ .bind( &abortAfterX, "no. failures" );
+
+ cli["-w"]["--warn"]
+ .describe( "enable warnings" )
+ .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+// cli.into( &setVerbosity )
+// .describe( "level of verbosity (0=no output)" )
+// .shortOpt( "v")
+// .longOpt( "verbosity" )
+// .placeholder( "level" );
+
+ cli[_]
+ .describe( "which test or tests to use" )
+ .bind( &addTestOrTags, "test name, pattern or tags" );
+
+ cli["-d"]["--durations"]
+ .describe( "show test durations" )
+ .bind( &setShowDurations, "yes|no" );
+
+ cli["-f"]["--input-file"]
+ .describe( "load test names to run from a file" )
+ .bind( &loadTestNamesFromFile, "filename" );
+
+ cli["-#"]["--filenames-as-tags"]
+ .describe( "adds a tag for the filename" )
+ .bind( &ConfigData::filenamesAsTags );
+
+ cli["-c"]["--section"]
+ .describe( "specify section to run" )
+ .bind( &addSectionToRun, "section name" );
+
+ // Less common commands which don't have a short form
+ cli["--list-test-names-only"]
+ .describe( "list all/matching test cases names only" )
+ .bind( &ConfigData::listTestNamesOnly );
+
+ cli["--list-reporters"]
+ .describe( "list all reporters" )
+ .bind( &ConfigData::listReporters );
+
+ cli["--order"]
+ .describe( "test case order (defaults to decl)" )
+ .bind( &setOrder, "decl|lex|rand" );
+
+ cli["--rng-seed"]
+ .describe( "set a specific seed for random numbers" )
+ .bind( &setRngSeed, "'time'|number" );
+
+ cli["--force-colour"]
+ .describe( "force colourised output (deprecated)" )
+ .bind( &forceColour );
+
+ cli["--use-colour"]
+ .describe( "should output be colourised" )
+ .bind( &setUseColour, "yes|no" );
+
+ return cli;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# endif
+# else
+# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ const std::string wrappableBeforeChars = "[({<\t";
+ const std::string wrappableAfterChars = "])}>-,./|\\";
+ const std::string wrappableInsteadOfChars = " \n\r";
+ std::string indent = _attr.initialIndent != std::string::npos
+ ? std::string( _attr.initialIndent, ' ' )
+ : std::string( _attr.indent, ' ' );
+
+ typedef std::string::const_iterator iterator;
+ iterator it = _str.begin();
+ const iterator strEnd = _str.end();
+
+ while( it != strEnd ) {
+
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+
+ std::string suffix;
+ std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
+ iterator itEnd = it+width;
+ iterator itNext = _str.end();
+
+ iterator itNewLine = std::find( it, itEnd, '\n' );
+ if( itNewLine != itEnd )
+ itEnd = itNewLine;
+
+ if( itEnd != strEnd ) {
+ bool foundWrapPoint = false;
+ iterator findIt = itEnd;
+ do {
+ if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
+ itEnd = findIt+1;
+ itNext = findIt+1;
+ foundWrapPoint = true;
+ }
+ else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
+ itEnd = findIt;
+ itNext = findIt;
+ foundWrapPoint = true;
+ }
+ else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
+ itNext = findIt+1;
+ itEnd = findIt;
+ foundWrapPoint = true;
+ }
+ if( findIt == it )
+ break;
+ else
+ --findIt;
+ }
+ while( !foundWrapPoint );
+
+ if( !foundWrapPoint ) {
+ // No good wrap char, so we'll break mid word and add a hyphen
+ --itEnd;
+ itNext = itEnd;
+ suffix = "-";
+ }
+ else {
+ while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
+ --itEnd;
+ }
+ }
+ lines.push_back( indent + std::string( it, itEnd ) + suffix );
+
+ if( indent.size() != _attr.indent )
+ indent = std::string( _attr.indent, ' ' );
+ it = itNext;
+ }
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+ using Tbc::Text;
+ using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = Yellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = Yellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+
+ // Use constructed object for RAII guard
+ Colour( Code _colourCode );
+ Colour( Colour const& other );
+ ~Colour();
+
+ // Use static method for one-shot changes
+ static void use( Code _colourCode );
+
+ private:
+ bool m_moved;
+ };
+
+ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+
+namespace Catch
+{
+ struct ReporterConfig {
+ explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& stream() const { return *m_stream; }
+ Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
+
+ private:
+ std::ostream* m_stream;
+ Ptr<IConfig const> m_fullConfig;
+ };
+
+ struct ReporterPreferences {
+ ReporterPreferences()
+ : shouldRedirectStdOut( false )
+ {}
+
+ bool shouldRedirectStdOut;
+ };
+
+ template<typename T>
+ struct LazyStat : Option<T> {
+ LazyStat() : used( false ) {}
+ LazyStat& operator=( T const& _value ) {
+ Option<T>::operator=( _value );
+ used = false;
+ return *this;
+ }
+ void reset() {
+ Option<T>::reset();
+ used = false;
+ }
+ bool used;
+ };
+
+ struct TestRunInfo {
+ TestRunInfo( std::string const& _name ) : name( _name ) {}
+ std::string name;
+ };
+ struct GroupInfo {
+ GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount )
+ : name( _name ),
+ groupIndex( _groupIndex ),
+ groupsCounts( _groupsCount )
+ {}
+
+ std::string name;
+ std::size_t groupIndex;
+ std::size_t groupsCounts;
+ };
+
+ struct AssertionStats {
+ AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
+ : assertionResult( _assertionResult ),
+ infoMessages( _infoMessages ),
+ totals( _totals )
+ {
+ if( assertionResult.hasMessage() ) {
+ // Copy message into messages list.
+ // !TBD This should have been done earlier, somewhere
+ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+ builder << assertionResult.getMessage();
+ builder.m_info.message = builder.m_stream.str();
+
+ infoMessages.push_back( builder.m_info );
+ }
+ }
+ virtual ~AssertionStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = default;
+ AssertionStats& operator = ( AssertionStats && ) = default;
+# endif
+
+ AssertionResult assertionResult;
+ std::vector<MessageInfo> infoMessages;
+ Totals totals;
+ };
+
+ struct SectionStats {
+ SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions )
+ : sectionInfo( _sectionInfo ),
+ assertions( _assertions ),
+ durationInSeconds( _durationInSeconds ),
+ missingAssertions( _missingAssertions )
+ {}
+ virtual ~SectionStats();
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SectionStats( SectionStats const& ) = default;
+ SectionStats( SectionStats && ) = default;
+ SectionStats& operator = ( SectionStats const& ) = default;
+ SectionStats& operator = ( SectionStats && ) = default;
+# endif
+
+ SectionInfo sectionInfo;
+ Counts assertions;
+ double durationInSeconds;
+ bool missingAssertions;
+ };
+
+ struct TestCaseStats {
+ TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting )
+ : testInfo( _testInfo ),
+ totals( _totals ),
+ stdOut( _stdOut ),
+ stdErr( _stdErr ),
+ aborting( _aborting )
+ {}
+ virtual ~TestCaseStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestCaseStats( TestCaseStats const& ) = default;
+ TestCaseStats( TestCaseStats && ) = default;
+ TestCaseStats& operator = ( TestCaseStats const& ) = default;
+ TestCaseStats& operator = ( TestCaseStats && ) = default;
+# endif
+
+ TestCaseInfo testInfo;
+ Totals totals;
+ std::string stdOut;
+ std::string stdErr;
+ bool aborting;
+ };
+
+ struct TestGroupStats {
+ TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : groupInfo( _groupInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ TestGroupStats( GroupInfo const& _groupInfo )
+ : groupInfo( _groupInfo ),
+ aborting( false )
+ {}
+ virtual ~TestGroupStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestGroupStats( TestGroupStats const& ) = default;
+ TestGroupStats( TestGroupStats && ) = default;
+ TestGroupStats& operator = ( TestGroupStats const& ) = default;
+ TestGroupStats& operator = ( TestGroupStats && ) = default;
+# endif
+
+ GroupInfo groupInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct TestRunStats {
+ TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : runInfo( _runInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ virtual ~TestRunStats();
+
+# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestRunStats( TestRunStats const& _other )
+ : runInfo( _other.runInfo ),
+ totals( _other.totals ),
+ aborting( _other.aborting )
+ {}
+# else
+ TestRunStats( TestRunStats const& ) = default;
+ TestRunStats( TestRunStats && ) = default;
+ TestRunStats& operator = ( TestRunStats const& ) = default;
+ TestRunStats& operator = ( TestRunStats && ) = default;
+# endif
+
+ TestRunInfo runInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ class MultipleReporters;
+
+ struct IStreamingReporter : IShared {
+ virtual ~IStreamingReporter();
+
+ // Implementing class must also provide the following static method:
+ // static std::string getDescription();
+
+ virtual ReporterPreferences getPreferences() const = 0;
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+ virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
+ };
+
+ struct IReporterFactory : IShared {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+
+ struct IReporterRegistry {
+ typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+ typedef std::vector<Ptr<IReporterFactory> > Listeners;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ virtual Listeners const& getListeners() const = 0;
+ };
+
+ Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+ inline std::size_t listTests( Config const& config ) {
+
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ Catch::cout() << "Matching test cases:\n";
+ else {
+ Catch::cout() << "All available test cases:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::size_t matchedTests = 0;
+ TextAttributes nameAttr, tagsAttr;
+ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+ tagsAttr.setIndent( 6 );
+
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ Colour colourGuard( colour );
+
+ Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+ if( !testCaseInfo.tags.empty() )
+ Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+ }
+
+ if( !config.testSpec().hasFilters() )
+ Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
+ else
+ Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
+ return matchedTests;
+ }
+
+ inline std::size_t listTestsNamesOnly( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( !config.testSpec().hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ if( startsWith( testCaseInfo.name, '#' ) )
+ Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl;
+ else
+ Catch::cout() << testCaseInfo.name << std::endl;
+ }
+ return matchedTests;
+ }
+
+ struct TagInfo {
+ TagInfo() : count ( 0 ) {}
+ void add( std::string const& spelling ) {
+ ++count;
+ spellings.insert( spelling );
+ }
+ std::string all() const {
+ std::string out;
+ for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+ it != itEnd;
+ ++it )
+ out += "[" + *it + "]";
+ return out;
+ }
+ std::set<std::string> spellings;
+ std::size_t count;
+ };
+
+ inline std::size_t listTags( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ Catch::cout() << "Tags for matching test cases:\n";
+ else {
+ Catch::cout() << "All available tags:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::map<std::string, TagInfo> tagCounts;
+
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
+ tagItEnd = it->getTestCaseInfo().tags.end();
+ tagIt != tagItEnd;
+ ++tagIt ) {
+ std::string tagName = *tagIt;
+ std::string lcaseTagName = toLower( tagName );
+ std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+ if( countIt == tagCounts.end() )
+ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+ countIt->second.add( tagName );
+ }
+ }
+
+ for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+ countItEnd = tagCounts.end();
+ countIt != countItEnd;
+ ++countIt ) {
+ std::ostringstream oss;
+ oss << " " << std::setw(2) << countIt->second.count << " ";
+ Text wrapper( countIt->second.all(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( oss.str().size() )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+ Catch::cout() << oss.str() << wrapper << '\n';
+ }
+ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+ return tagCounts.size();
+ }
+
+ inline std::size_t listReporters( Config const& /*config*/ ) {
+ Catch::cout() << "Available reporters:\n";
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+ std::size_t maxNameLen = 0;
+ for(it = itBegin; it != itEnd; ++it )
+ maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+ for(it = itBegin; it != itEnd; ++it ) {
+ Text wrapper( it->second->getDescription(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( 7+maxNameLen )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+ Catch::cout() << " "
+ << it->first
+ << ':'
+ << std::string( maxNameLen - it->first.size() + 2, ' ' )
+ << wrapper << '\n';
+ }
+ Catch::cout() << std::endl;
+ return factories.size();
+ }
+
+ inline Option<std::size_t> list( Config const& config ) {
+ Option<std::size_t> listedCount;
+ if( config.listTests() )
+ listedCount = listedCount.valueOr(0) + listTests( config );
+ if( config.listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+ if( config.listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( config );
+ if( config.listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters( config );
+ return listedCount;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_run_context.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <algorithm>
+#include <string>
+#include <assert.h>
+#include <vector>
+#include <stdexcept>
+
+CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+
+namespace Catch {
+namespace TestCaseTracking {
+
+ struct NameAndLocation {
+ std::string name;
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+ : name( _name ),
+ location( _location )
+ {}
+ };
+
+ struct ITracker : SharedImpl<> {
+ virtual ~ITracker();
+
+ // static queries
+ virtual NameAndLocation const& nameAndLocation() const = 0;
+
+ // dynamic queries
+ virtual bool isComplete() const = 0; // Successfully completed or failed
+ virtual bool isSuccessfullyCompleted() const = 0;
+ virtual bool isOpen() const = 0; // Started but not complete
+ virtual bool hasChildren() const = 0;
+
+ virtual ITracker& parent() = 0;
+
+ // actions
+ virtual void close() = 0; // Successfully complete
+ virtual void fail() = 0;
+ virtual void markAsNeedingAnotherRun() = 0;
+
+ virtual void addChild( Ptr<ITracker> const& child ) = 0;
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
+ virtual void openChild() = 0;
+
+ // Debug/ checking
+ virtual bool isSectionTracker() const = 0;
+ virtual bool isIndexTracker() const = 0;
+ };
+
+ class TrackerContext {
+
+ enum RunState {
+ NotStarted,
+ Executing,
+ CompletedCycle
+ };
+
+ Ptr<ITracker> m_rootTracker;
+ ITracker* m_currentTracker;
+ RunState m_runState;
+
+ public:
+
+ static TrackerContext& instance() {
+ static TrackerContext s_instance;
+ return s_instance;
+ }
+
+ TrackerContext()
+ : m_currentTracker( CATCH_NULL ),
+ m_runState( NotStarted )
+ {}
+
+ ITracker& startRun();
+
+ void endRun() {
+ m_rootTracker.reset();
+ m_currentTracker = CATCH_NULL;
+ m_runState = NotStarted;
+ }
+
+ void startCycle() {
+ m_currentTracker = m_rootTracker.get();
+ m_runState = Executing;
+ }
+ void completeCycle() {
+ m_runState = CompletedCycle;
+ }
+
+ bool completedCycle() const {
+ return m_runState == CompletedCycle;
+ }
+ ITracker& currentTracker() {
+ return *m_currentTracker;
+ }
+ void setCurrentTracker( ITracker* tracker ) {
+ m_currentTracker = tracker;
+ }
+ };
+
+ class TrackerBase : public ITracker {
+ protected:
+ enum CycleState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ NeedsAnotherRun,
+ CompletedSuccessfully,
+ Failed
+ };
+ class TrackerHasName {
+ NameAndLocation m_nameAndLocation;
+ public:
+ TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
+ bool operator ()( Ptr<ITracker> const& tracker ) {
+ return
+ tracker->nameAndLocation().name == m_nameAndLocation.name &&
+ tracker->nameAndLocation().location == m_nameAndLocation.location;
+ }
+ };
+ typedef std::vector<Ptr<ITracker> > Children;
+ NameAndLocation m_nameAndLocation;
+ TrackerContext& m_ctx;
+ ITracker* m_parent;
+ Children m_children;
+ CycleState m_runState;
+ public:
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : m_nameAndLocation( nameAndLocation ),
+ m_ctx( ctx ),
+ m_parent( parent ),
+ m_runState( NotStarted )
+ {}
+ virtual ~TrackerBase();
+
+ virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
+ return m_nameAndLocation;
+ }
+ virtual bool isComplete() const CATCH_OVERRIDE {
+ return m_runState == CompletedSuccessfully || m_runState == Failed;
+ }
+ virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+ return m_runState == CompletedSuccessfully;
+ }
+ virtual bool isOpen() const CATCH_OVERRIDE {
+ return m_runState != NotStarted && !isComplete();
+ }
+ virtual bool hasChildren() const CATCH_OVERRIDE {
+ return !m_children.empty();
+ }
+
+ virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+ m_children.push_back( child );
+ }
+
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
+ Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
+ return( it != m_children.end() )
+ ? it->get()
+ : CATCH_NULL;
+ }
+ virtual ITracker& parent() CATCH_OVERRIDE {
+ assert( m_parent ); // Should always be non-null except for root
+ return *m_parent;
+ }
+
+ virtual void openChild() CATCH_OVERRIDE {
+ if( m_runState != ExecutingChildren ) {
+ m_runState = ExecutingChildren;
+ if( m_parent )
+ m_parent->openChild();
+ }
+ }
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
+ void open() {
+ m_runState = Executing;
+ moveToThis();
+ if( m_parent )
+ m_parent->openChild();
+ }
+
+ virtual void close() CATCH_OVERRIDE {
+
+ // Close any still open children (e.g. generators)
+ while( &m_ctx.currentTracker() != this )
+ m_ctx.currentTracker().close();
+
+ switch( m_runState ) {
+ case NotStarted:
+ case CompletedSuccessfully:
+ case Failed:
+ throw std::logic_error( "Illogical state" );
+
+ case NeedsAnotherRun:
+ break;;
+
+ case Executing:
+ m_runState = CompletedSuccessfully;
+ break;
+ case ExecutingChildren:
+ if( m_children.empty() || m_children.back()->isComplete() )
+ m_runState = CompletedSuccessfully;
+ break;
+
+ default:
+ throw std::logic_error( "Unexpected state" );
+ }
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ virtual void fail() CATCH_OVERRIDE {
+ m_runState = Failed;
+ if( m_parent )
+ m_parent->markAsNeedingAnotherRun();
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+ m_runState = NeedsAnotherRun;
+ }
+ private:
+ void moveToParent() {
+ assert( m_parent );
+ m_ctx.setCurrentTracker( m_parent );
+ }
+ void moveToThis() {
+ m_ctx.setCurrentTracker( this );
+ }
+ };
+
+ class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
+ public:
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( nameAndLocation, ctx, parent )
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+ parent = &parent->parent();
+
+ SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+ addNextFilters( parentSection.m_filters );
+ }
+ }
+ virtual ~SectionTracker();
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+ SectionTracker* section = CATCH_NULL;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isSectionTracker() );
+ section = static_cast<SectionTracker*>( childTracker );
+ }
+ else {
+ section = new SectionTracker( nameAndLocation, ctx, &currentTracker );
+ currentTracker.addChild( section );
+ }
+ if( !ctx.completedCycle() )
+ section->tryOpen();
+ return *section;
+ }
+
+ void tryOpen() {
+ if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
+ open();
+ }
+
+ void addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+ m_filters.push_back(""); // Root - should never be consulted
+ m_filters.push_back(""); // Test Case - not a section filter
+ m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+ }
+ }
+ void addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+ m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+ }
+ };
+
+ class IndexTracker : public TrackerBase {
+ int m_size;
+ int m_index;
+ public:
+ IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+ : TrackerBase( nameAndLocation, ctx, parent ),
+ m_size( size ),
+ m_index( -1 )
+ {}
+ virtual ~IndexTracker();
+
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
+ static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+ IndexTracker* tracker = CATCH_NULL;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isIndexTracker() );
+ tracker = static_cast<IndexTracker*>( childTracker );
+ }
+ else {
+ tracker = new IndexTracker( nameAndLocation, ctx, &currentTracker, size );
+ currentTracker.addChild( tracker );
+ }
+
+ if( !ctx.completedCycle() && !tracker->isComplete() ) {
+ if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+ tracker->moveNext();
+ tracker->open();
+ }
+
+ return *tracker;
+ }
+
+ int index() const { return m_index; }
+
+ void moveNext() {
+ m_index++;
+ m_children.clear();
+ }
+
+ virtual void close() CATCH_OVERRIDE {
+ TrackerBase::close();
+ if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+ m_runState = Executing;
+ }
+ };
+
+ inline ITracker& TrackerContext::startRun() {
+ m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
+ m_currentTracker = CATCH_NULL;
+ m_runState = Executing;
+ return *m_rootTracker;
+ }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
+
+} // namespace Catch
+
+CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+ // Report the error condition
+ inline void reportFatal( std::string const& message ) {
+ IContext& context = Catch::getCurrentContext();
+ IResultCapture* resultCapture = context.getResultCapture();
+ resultCapture->handleFatalErrorCondition( message );
+ }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+// #included from: catch_windows_h_proxy.h
+
+#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# define NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+
+# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_WINDOWS_SEH is defined
+
+namespace Catch {
+
+ struct SignalDefs { DWORD id; const char* name; };
+ extern SignalDefs signalDefs[];
+ // There is no 1-1 mapping between signals and windows exceptions.
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ SignalDefs signalDefs[] = {
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
+ { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+ { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
+ };
+
+ struct FatalConditionHandler {
+
+ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+ reportFatal(signalDefs[i].name);
+ }
+ }
+ // If its not an exception we care about, pass it along.
+ // This stops us from eating debugger breaks etc.
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ FatalConditionHandler() {
+ isSet = true;
+ // 32k seems enough for Catch to handle stack overflow,
+ // but the value was found experimentally, so there is no strong guarantee
+ guaranteeSize = 32 * 1024;
+ exceptionHandlerHandle = CATCH_NULL;
+ // Register as first handler in current chain
+ exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+ // Pass in guarantee size to be filled
+ SetThreadStackGuarantee(&guaranteeSize);
+ }
+
+ static void reset() {
+ if (isSet) {
+ // Unregister handler and restore the old guarantee
+ RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+ SetThreadStackGuarantee(&guaranteeSize);
+ exceptionHandlerHandle = CATCH_NULL;
+ isSet = false;
+ }
+ }
+
+ ~FatalConditionHandler() {
+ reset();
+ }
+ private:
+ static bool isSet;
+ static ULONG guaranteeSize;
+ static PVOID exceptionHandlerHandle;
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ ULONG FatalConditionHandler::guaranteeSize = 0;
+ PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
+
+} // namespace Catch
+
+# endif // CATCH_CONFIG_WINDOWS_SEH
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
+#include <signal.h>
+
+namespace Catch {
+
+ struct SignalDefs {
+ int id;
+ const char* name;
+ };
+ extern SignalDefs signalDefs[];
+ SignalDefs signalDefs[] = {
+ { SIGINT, "SIGINT - Terminal interrupt signal" },
+ { SIGILL, "SIGILL - Illegal instruction signal" },
+ { SIGFPE, "SIGFPE - Floating point error signal" },
+ { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+ { SIGTERM, "SIGTERM - Termination request signal" },
+ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+ };
+
+ struct FatalConditionHandler {
+
+ static bool isSet;
+ static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
+ static stack_t oldSigStack;
+ static char altStackMem[SIGSTKSZ];
+
+ static void handleSignal( int sig ) {
+ std::string name = "<unknown signal>";
+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ SignalDefs &def = signalDefs[i];
+ if (sig == def.id) {
+ name = def.name;
+ break;
+ }
+ }
+ reset();
+ reportFatal(name);
+ raise( sig );
+ }
+
+ FatalConditionHandler() {
+ isSet = true;
+ stack_t sigStack;
+ sigStack.ss_sp = altStackMem;
+ sigStack.ss_size = SIGSTKSZ;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = { 0 };
+
+ sa.sa_handler = handleSignal;
+ sa.sa_flags = SA_ONSTACK;
+ for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+ }
+ }
+
+ ~FatalConditionHandler() {
+ reset();
+ }
+ static void reset() {
+ if( isSet ) {
+ // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, CATCH_NULL);
+ isSet = false;
+ }
+ }
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+ stack_t FatalConditionHandler::oldSigStack = {};
+ char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
+} // namespace Catch
+
+# endif // CATCH_CONFIG_POSIX_SIGNALS
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+ class StreamRedirect {
+
+ public:
+ StreamRedirect( std::ostream& stream, std::string& targetString )
+ : m_stream( stream ),
+ m_prevBuf( stream.rdbuf() ),
+ m_targetString( targetString )
+ {
+ stream.rdbuf( m_oss.rdbuf() );
+ }
+
+ ~StreamRedirect() {
+ m_targetString += m_oss.str();
+ m_stream.rdbuf( m_prevBuf );
+ }
+
+ private:
+ std::ostream& m_stream;
+ std::streambuf* m_prevBuf;
+ std::ostringstream m_oss;
+ std::string& m_targetString;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class RunContext : public IResultCapture, public IRunner {
+
+ RunContext( RunContext const& );
+ void operator =( RunContext const& );
+
+ public:
+
+ explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+ : m_runInfo( _config->name() ),
+ m_context( getCurrentMutableContext() ),
+ m_activeTestCase( CATCH_NULL ),
+ m_config( _config ),
+ m_reporter( reporter ),
+ m_shouldReportUnexpected ( true )
+ {
+ m_context.setRunner( this );
+ m_context.setConfig( m_config );
+ m_context.setResultCapture( this );
+ m_reporter->testRunStarting( m_runInfo );
+ }
+
+ virtual ~RunContext() {
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+ }
+
+ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+ }
+ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+ }
+
+ Totals runTest( TestCase const& testCase ) {
+ Totals prevTotals = m_totals;
+
+ std::string redirectedCout;
+ std::string redirectedCerr;
+
+ TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+ m_reporter->testCaseStarting( testInfo );
+
+ m_activeTestCase = &testCase;
+
+ do {
+ ITracker& rootTracker = m_trackerContext.startRun();
+ assert( rootTracker.isSectionTracker() );
+ static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
+ do {
+ m_trackerContext.startCycle();
+ m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
+ runCurrentTest( redirectedCout, redirectedCerr );
+ }
+ while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
+ }
+ // !TBD: deprecated - this will be replaced by indexed trackers
+ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+ Totals deltaTotals = m_totals.delta( prevTotals );
+ if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+ deltaTotals.assertions.failed++;
+ deltaTotals.testCases.passed--;
+ deltaTotals.testCases.failed++;
+ }
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting() ) );
+
+ m_activeTestCase = CATCH_NULL;
+ m_testCaseTracker = CATCH_NULL;
+
+ return deltaTotals;
+ }
+
+ Ptr<IConfig const> config() const {
+ return m_config;
+ }
+
+ private: // IResultCapture
+
+ virtual void assertionEnded( AssertionResult const& result ) {
+ if( result.getResultType() == ResultWas::Ok ) {
+ m_totals.assertions.passed++;
+ }
+ else if( !result.isOk() ) {
+ m_totals.assertions.failed++;
+ }
+
+ // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+ // and should be let to clear themselves out.
+ static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+ // Reset working state
+ m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+ m_lastResult = result;
+ }
+
+ virtual bool sectionStarted (
+ SectionInfo const& sectionInfo,
+ Counts& assertions
+ )
+ {
+ ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
+ if( !sectionTracker.isOpen() )
+ return false;
+ m_activeSections.push_back( &sectionTracker );
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting( sectionInfo );
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ bool testForMissingAssertions( Counts& assertions ) {
+ if( assertions.total() != 0 )
+ return false;
+ if( !m_config->warnAboutMissingAssertions() )
+ return false;
+ if( m_trackerContext.currentTracker().hasChildren() )
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+ Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( !m_activeSections.empty() ) {
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+ }
+
+ m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
+ m_messages.clear();
+ }
+
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+ if( m_unfinishedSections.empty() )
+ m_activeSections.back()->fail();
+ else
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+
+ m_unfinishedSections.push_back( endInfo );
+ }
+
+ virtual void pushScopedMessage( MessageInfo const& message ) {
+ m_messages.push_back( message );
+ }
+
+ virtual void popScopedMessage( MessageInfo const& message ) {
+ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+ }
+
+ virtual std::string getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+ : std::string();
+ }
+
+ virtual const AssertionResult* getLastResult() const {
+ return &m_lastResult;
+ }
+
+ virtual void exceptionEarlyReported() {
+ m_shouldReportUnexpected = false;
+ }
+
+ virtual void handleFatalErrorCondition( std::string const& message ) {
+ // Don't rebuild the result -- the stringification itself can cause more fatal errors
+ // Instead, fake a result data.
+ AssertionResultData tempResult;
+ tempResult.resultType = ResultWas::FatalErrorCondition;
+ tempResult.message = message;
+ AssertionResult result(m_lastAssertionInfo, tempResult);
+
+ getResultCapture().assertionEnded(result);
+
+ handleUnfinishedSections();
+
+ // Recreate section for test case (as we will lose the one that was in scope)
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+
+ Counts assertions;
+ assertions.failed = 1;
+ SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+ m_reporter->sectionEnded( testCaseSectionStats );
+
+ TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+ Totals deltaTotals;
+ deltaTotals.testCases.failed = 1;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ std::string(),
+ std::string(),
+ false ) );
+ m_totals.testCases.failed++;
+ testGroupEnded( std::string(), m_totals, 1, 1 );
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+ }
+
+ public:
+ // !TBD We need to do this another way!
+ bool aborting() const {
+ return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+ }
+
+ private:
+
+ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+ m_reporter->sectionStarting( testCaseSection );
+ Counts prevAssertions = m_totals.assertions;
+ double duration = 0;
+ m_shouldReportUnexpected = true;
+ try {
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal );
+
+ seedRng( *m_config );
+
+ Timer timer;
+ timer.start();
+ if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+ StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+ StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
+ invokeActiveTestCase();
+ }
+ else {
+ invokeActiveTestCase();
+ }
+ duration = timer.getElapsedSeconds();
+ }
+ catch( TestFailureException& ) {
+ // This just means the test was aborted due to failure
+ }
+ catch(...) {
+ // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+ // are reported without translation at the point of origin.
+ if (m_shouldReportUnexpected) {
+ makeUnexpectedResultBuilder().useActiveException();
+ }
+ }
+ m_testCaseTracker->close();
+ handleUnfinishedSections();
+ m_messages.clear();
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( testCaseInfo.okToFail() ) {
+ std::swap( assertions.failedButOk, assertions.failed );
+ m_totals.assertions.failed -= assertions.failedButOk;
+ m_totals.assertions.failedButOk += assertions.failedButOk;
+ }
+
+ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+ m_reporter->sectionEnded( testCaseSectionStats );
+ }
+
+ void invokeActiveTestCase() {
+ FatalConditionHandler fatalConditionHandler; // Handle signals
+ m_activeTestCase->invoke();
+ fatalConditionHandler.reset();
+ }
+
+ private:
+
+ ResultBuilder makeUnexpectedResultBuilder() const {
+ return ResultBuilder( m_lastAssertionInfo.macroName.c_str(),
+ m_lastAssertionInfo.lineInfo,
+ m_lastAssertionInfo.capturedExpression.c_str(),
+ m_lastAssertionInfo.resultDisposition );
+ }
+
+ void handleUnfinishedSections() {
+ // If sections ended prematurely due to an exception we stored their
+ // infos here so we can tear them down outside the unwind process.
+ for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it )
+ sectionEnded( *it );
+ m_unfinishedSections.clear();
+ }
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase;
+ ITracker* m_testCaseTracker;
+ ITracker* m_currentSectionTracker;
+ AssertionResult m_lastResult;
+
+ Ptr<IConfig const> m_config;
+ Totals m_totals;
+ Ptr<IStreamingReporter> m_reporter;
+ std::vector<MessageInfo> m_messages;
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<SectionEndInfo> m_unfinishedSections;
+ std::vector<ITracker*> m_activeSections;
+ TrackerContext m_trackerContext;
+ bool m_shouldReportUnexpected;
+ };
+
+ IResultCapture& getResultCapture() {
+ if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+ return *capture;
+ else
+ throw std::logic_error( "No result capture instance" );
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+ // Versioning information
+ struct Version {
+ Version( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber );
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const patchNumber;
+
+ // buildNumber is only used if branchName is not null
+ char const * const branchName;
+ unsigned int const buildNumber;
+
+ friend std::ostream& operator << ( std::ostream& os, Version const& version );
+
+ private:
+ void operator=( Version const& );
+ };
+
+ inline Version libraryVersion();
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+ Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+ Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+ if( !reporter ) {
+ std::ostringstream oss;
+ oss << "No reporter registered with name: '" << reporterName << "'";
+ throw std::domain_error( oss.str() );
+ }
+ return reporter;
+ }
+
+ Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+ std::vector<std::string> reporters = config->getReporterNames();
+ if( reporters.empty() )
+ reporters.push_back( "console" );
+
+ Ptr<IStreamingReporter> reporter;
+ for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+ it != itEnd;
+ ++it )
+ reporter = addReporter( reporter, createReporter( *it, config ) );
+ return reporter;
+ }
+ Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+ IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+ for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+ it != itEnd;
+ ++it )
+ reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+ return reporters;
+ }
+
+ Totals runTests( Ptr<Config> const& config ) {
+
+ Ptr<IConfig const> iconfig = config.get();
+
+ Ptr<IStreamingReporter> reporter = makeReporter( config );
+ reporter = addListeners( iconfig, reporter );
+
+ RunContext context( iconfig, reporter );
+
+ Totals totals;
+
+ context.testGroupStarting( config->name(), 1, 1 );
+
+ TestSpec testSpec = config->testSpec();
+ if( !testSpec.hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+ std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+ for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+ it != itEnd;
+ ++it ) {
+ if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+ totals += context.runTest( *it );
+ else
+ reporter->skipTest( *it );
+ }
+
+ context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+ return totals;
+ }
+
+ void applyFilenamesAsTags( IConfig const& config ) {
+ std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+ for(std::size_t i = 0; i < tests.size(); ++i ) {
+ TestCase& test = const_cast<TestCase&>( tests[i] );
+ std::set<std::string> tags = test.tags;
+
+ std::string filename = test.lineInfo.file;
+ std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+ if( lastSlash != std::string::npos )
+ filename = filename.substr( lastSlash+1 );
+
+ std::string::size_type lastDot = filename.find_last_of( "." );
+ if( lastDot != std::string::npos )
+ filename = filename.substr( 0, lastDot );
+
+ tags.insert( "#" + filename );
+ setTags( test, tags );
+ }
+ }
+
+ class Session : NonCopyable {
+ static bool alreadyInstantiated;
+
+ public:
+
+ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+ Session()
+ : m_cli( makeCommandLineParser() ) {
+ if( alreadyInstantiated ) {
+ std::string msg = "Only one instance of Catch::Session can ever be used";
+ Catch::cerr() << msg << std::endl;
+ throw std::logic_error( msg );
+ }
+ alreadyInstantiated = true;
+ }
+ ~Session() {
+ Catch::cleanUp();
+ }
+
+ void showHelp( std::string const& processName ) {
+ Catch::cout() << "\nCatch v" << libraryVersion() << "\n";
+
+ m_cli.usage( Catch::cout(), processName );
+ Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+ }
+
+ int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ try {
+ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+ m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
+ if( m_configData.showHelp )
+ showHelp( m_configData.processName );
+ m_config.reset();
+ }
+ catch( std::exception& ex ) {
+ {
+ Colour colourGuard( Colour::Red );
+ Catch::cerr()
+ << "\nError(s) in input:\n"
+ << Text( ex.what(), TextAttributes().setIndent(2) )
+ << "\n\n";
+ }
+ m_cli.usage( Catch::cout(), m_configData.processName );
+ return (std::numeric_limits<int>::max)();
+ }
+ return 0;
+ }
+
+ void useConfigData( ConfigData const& _configData ) {
+ m_configData = _configData;
+ m_config.reset();
+ }
+
+ int run( int argc, char const* const* const argv ) {
+
+ int returnCode = applyCommandLine( argc, argv );
+ if( returnCode == 0 )
+ returnCode = run();
+ return returnCode;
+ }
+
+ #if defined(WIN32) && defined(UNICODE)
+ int run( int argc, wchar_t const* const* const argv ) {
+
+ char **utf8Argv = new char *[ argc ];
+
+ for ( int i = 0; i < argc; ++i ) {
+ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
+
+ utf8Argv[ i ] = new char[ bufSize ];
+
+ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
+ }
+
+ int returnCode = applyCommandLine( argc, utf8Argv );
+ if( returnCode == 0 )
+ returnCode = run();
+
+ for ( int i = 0; i < argc; ++i )
+ delete [] utf8Argv[ i ];
+
+ delete [] utf8Argv;
+
+ return returnCode;
+ }
+ #endif
+
+ int run() {
+ if( m_configData.showHelp )
+ return 0;
+
+ try
+ {
+ config(); // Force config to be constructed
+
+ seedRng( *m_config );
+
+ if( m_configData.filenamesAsTags )
+ applyFilenamesAsTags( *m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( config() ) )
+ return static_cast<int>( *listed );
+
+ return static_cast<int>( runTests( m_config ).assertions.failed );
+ }
+ catch( std::exception& ex ) {
+ Catch::cerr() << ex.what() << std::endl;
+ return (std::numeric_limits<int>::max)();
+ }
+ }
+
+ Clara::CommandLine<ConfigData> const& cli() const {
+ return m_cli;
+ }
+ std::vector<Clara::Parser::Token> const& unusedTokens() const {
+ return m_unusedTokens;
+ }
+ ConfigData& configData() {
+ return m_configData;
+ }
+ Config& config() {
+ if( !m_config )
+ m_config = new Config( m_configData );
+ return *m_config;
+ }
+ private:
+ Clara::CommandLine<ConfigData> m_cli;
+ std::vector<Clara::Parser::Token> m_unusedTokens;
+ ConfigData m_configData;
+ Ptr<Config> m_config;
+ };
+
+ bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+ struct RandomNumberGenerator {
+ typedef std::ptrdiff_t result_type;
+
+ result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return 1000000; }
+ result_type operator()() const { return std::rand() % max(); }
+#endif
+ template<typename V>
+ static void shuffle( V& vector ) {
+ RandomNumberGenerator rng;
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+ std::shuffle( vector.begin(), vector.end(), rng );
+#else
+ std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+ }
+ };
+
+ inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+ std::vector<TestCase> sorted = unsortedTestCases;
+
+ switch( config.runOrder() ) {
+ case RunTests::InLexicographicalOrder:
+ std::sort( sorted.begin(), sorted.end() );
+ break;
+ case RunTests::InRandomOrder:
+ {
+ seedRng( config );
+ RandomNumberGenerator::shuffle( sorted );
+ }
+ break;
+ case RunTests::InDeclarationOrder:
+ // already in declaration order
+ break;
+ }
+ return sorted;
+ }
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+ return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+ }
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+ std::set<TestCase> seenFunctions;
+ for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+ it != itEnd;
+ ++it ) {
+ std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+ if( !prev.second ) {
+ std::ostringstream ss;
+
+ ss << Colour( Colour::Red )
+ << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
+ << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+
+ throw std::runtime_error(ss.str());
+ }
+ }
+ }
+
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+ std::vector<TestCase> filtered;
+ filtered.reserve( testCases.size() );
+ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+ it != itEnd;
+ ++it )
+ if( matchTest( *it, testSpec, config ) )
+ filtered.push_back( *it );
+ return filtered;
+ }
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+ return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+ }
+
+ class TestRegistry : public ITestCaseRegistry {
+ public:
+ TestRegistry()
+ : m_currentSortOrder( RunTests::InDeclarationOrder ),
+ m_unnamedCount( 0 )
+ {}
+ virtual ~TestRegistry();
+
+ virtual void registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name.empty() ) {
+ std::ostringstream oss;
+ oss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( oss.str() ) );
+ }
+ m_functions.push_back( testCase );
+ }
+
+ virtual std::vector<TestCase> const& getAllTests() const {
+ return m_functions;
+ }
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+ if( m_sortedFunctions.empty() )
+ enforceNoDuplicateTestCases( m_functions );
+
+ if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+ m_sortedFunctions = sortTests( config, m_functions );
+ m_currentSortOrder = config.runOrder();
+ }
+ return m_sortedFunctions;
+ }
+
+ private:
+ std::vector<TestCase> m_functions;
+ mutable RunTests::InWhatOrder m_currentSortOrder;
+ mutable std::vector<TestCase> m_sortedFunctions;
+ size_t m_unnamedCount;
+ std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+ public:
+
+ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+ virtual void invoke() const {
+ m_fun();
+ }
+
+ private:
+ virtual ~FreeFunctionTestCase();
+
+ TestFunction m_fun;
+ };
+
+ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+ std::string className = classOrQualifiedMethodName;
+ if( startsWith( className, '&' ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+ if( penultimateColons == std::string::npos )
+ penultimateColons = 1;
+ className = className.substr( penultimateColons, lastColons-penultimateColons );
+ }
+ return className;
+ }
+
+ void registerTestCase
+ ( ITestCase* testCase,
+ char const* classOrQualifiedMethodName,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ getMutableRegistryHub().registerTest
+ ( makeTestCase
+ ( testCase,
+ extractClassName( classOrQualifiedMethodName ),
+ nameAndDesc.name,
+ nameAndDesc.description,
+ lineInfo ) );
+ }
+ void registerTestCaseFunction
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ AutoReg::AutoReg
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCaseFunction( function, lineInfo, nameAndDesc );
+ }
+
+ AutoReg::~AutoReg() {}
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class ReporterRegistry : public IReporterRegistry {
+
+ public:
+
+ virtual ~ReporterRegistry() CATCH_OVERRIDE {}
+
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
+ FactoryMap::const_iterator it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return CATCH_NULL;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
+ m_factories.insert( std::make_pair( name, factory ) );
+ }
+ void registerListener( Ptr<IReporterFactory> const& factory ) {
+ m_listeners.push_back( factory );
+ }
+
+ virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
+ return m_factories;
+ }
+ virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+ return m_listeners;
+ }
+
+ private:
+ FactoryMap m_factories;
+ Listeners m_listeners;
+ };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+ public:
+ ~ExceptionTranslatorRegistry() {
+ deleteAll( m_translators );
+ }
+
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_translators.push_back( translator );
+ }
+
+ virtual std::string translateActiveException() const {
+ try {
+#ifdef __OBJC__
+ // In Objective-C try objective-c exceptions first
+ @try {
+ return tryTranslators();
+ }
+ @catch (NSException *exception) {
+ return Catch::toString( [exception description] );
+ }
+#else
+ return tryTranslators();
+#endif
+ }
+ catch( TestFailureException& ) {
+ throw;
+ }
+ catch( std::exception& ex ) {
+ return ex.what();
+ }
+ catch( std::string& msg ) {
+ return msg;
+ }
+ catch( const char* msg ) {
+ return msg;
+ }
+ catch(...) {
+ return "Unknown exception";
+ }
+ }
+
+ std::string tryTranslators() const {
+ if( m_translators.empty() )
+ throw;
+ else
+ return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
+ }
+
+ private:
+ std::vector<const IExceptionTranslator*> m_translators;
+ };
+}
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class TagAliasRegistry : public ITagAliasRegistry {
+ public:
+ virtual ~TagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+ void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+ RegistryHub( RegistryHub const& );
+ void operator=( RegistryHub const& );
+
+ public: // IRegistryHub
+ RegistryHub() {
+ }
+ virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
+ return m_reporterRegistry;
+ }
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
+ return m_testCaseRegistry;
+ }
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
+ return m_exceptionTranslatorRegistry;
+ }
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE {
+ return m_tagAliasRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+ m_reporterRegistry.registerListener( factory );
+ }
+ virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE {
+ m_tagAliasRegistry.add( alias, tag, lineInfo );
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ TagAliasRegistry m_tagAliasRegistry;
+ };
+
+ // Single, global, instance
+ inline RegistryHub*& getTheRegistryHub() {
+ static RegistryHub* theRegistryHub = CATCH_NULL;
+ if( !theRegistryHub )
+ theRegistryHub = new RegistryHub();
+ return theRegistryHub;
+ }
+ }
+
+ IRegistryHub& getRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ void cleanUp() {
+ delete getTheRegistryHub();
+ getTheRegistryHub() = CATCH_NULL;
+ cleanUpContext();
+ }
+ std::string translateActiveException() {
+ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <sstream>
+
+namespace Catch {
+
+ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+ : m_lineInfo( lineInfo ) {
+ std::ostringstream oss;
+ oss << lineInfo << ": function ";
+ oss << "not implemented";
+ m_what = oss.str();
+ }
+
+ const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+ return m_what.c_str();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+namespace Catch {
+
+ template<typename WriterF, size_t bufferSize=256>
+ class StreamBufImpl : public StreamBufBase {
+ char data[bufferSize];
+ WriterF m_writer;
+
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() CATCH_NOEXCEPT {
+ sync();
+ }
+
+ private:
+ int overflow( int c ) {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ FileStream::FileStream( std::string const& filename ) {
+ m_ofs.open( filename.c_str() );
+ if( m_ofs.fail() ) {
+ std::ostringstream oss;
+ oss << "Unable to open file: '" << filename << '\'';
+ throw std::domain_error( oss.str() );
+ }
+ }
+
+ std::ostream& FileStream::stream() const {
+ return m_ofs;
+ }
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ DebugOutStream::DebugOutStream()
+ : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+ m_os( m_streamBuf.get() )
+ {}
+
+ std::ostream& DebugOutStream::stream() const {
+ return m_os;
+ }
+
+ // Store the streambuf from cout up-front because
+ // cout may get redirected when running tests
+ CoutStream::CoutStream()
+ : m_os( Catch::cout().rdbuf() )
+ {}
+
+ std::ostream& CoutStream::stream() const {
+ return m_os;
+ }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+ std::ostream& cout() {
+ return std::cout;
+ }
+ std::ostream& cerr() {
+ return std::cerr;
+ }
+#endif
+}
+
+namespace Catch {
+
+ class Context : public IMutableContext {
+
+ Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
+ Context( Context const& );
+ void operator=( Context const& );
+
+ public:
+ virtual ~Context() {
+ deleteAllValues( m_generatorsByTestName );
+ }
+
+ public: // IContext
+ virtual IResultCapture* getResultCapture() {
+ return m_resultCapture;
+ }
+ virtual IRunner* getRunner() {
+ return m_runner;
+ }
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+ return getGeneratorsForCurrentTest()
+ .getGeneratorInfo( fileInfo, totalSize )
+ .getCurrentIndex();
+ }
+ virtual bool advanceGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ return generators && generators->moveNext();
+ }
+
+ virtual Ptr<IConfig const> getConfig() const {
+ return m_config;
+ }
+
+ public: // IMutableContext
+ virtual void setResultCapture( IResultCapture* resultCapture ) {
+ m_resultCapture = resultCapture;
+ }
+ virtual void setRunner( IRunner* runner ) {
+ m_runner = runner;
+ }
+ virtual void setConfig( Ptr<IConfig const> const& config ) {
+ m_config = config;
+ }
+
+ friend IMutableContext& getCurrentMutableContext();
+
+ private:
+ IGeneratorsForTest* findGeneratorsForCurrentTest() {
+ std::string testName = getResultCapture()->getCurrentTestName();
+
+ std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+ m_generatorsByTestName.find( testName );
+ return it != m_generatorsByTestName.end()
+ ? it->second
+ : CATCH_NULL;
+ }
+
+ IGeneratorsForTest& getGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ if( !generators ) {
+ std::string testName = getResultCapture()->getCurrentTestName();
+ generators = createGeneratorsForTest();
+ m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+ }
+ return *generators;
+ }
+
+ private:
+ Ptr<IConfig const> m_config;
+ IRunner* m_runner;
+ IResultCapture* m_resultCapture;
+ std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+ };
+
+ namespace {
+ Context* currentContext = CATCH_NULL;
+ }
+ IMutableContext& getCurrentMutableContext() {
+ if( !currentContext )
+ currentContext = new Context();
+ return *currentContext;
+ }
+ IContext& getCurrentContext() {
+ return getCurrentMutableContext();
+ }
+
+ void cleanUpContext() {
+ delete currentContext;
+ currentContext = CATCH_NULL;
+ }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+// #included from: catch_errno_guard.hpp
+#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED
+
+#include <cerrno>
+
+namespace Catch {
+
+ class ErrnoGuard {
+ public:
+ ErrnoGuard():m_oldErrno(errno){}
+ ~ErrnoGuard() { errno = m_oldErrno; }
+ private:
+ int m_oldErrno;
+ };
+
+}
+
+namespace Catch {
+ namespace {
+
+ struct IColourImpl {
+ virtual ~IColourImpl() {}
+ virtual void use( Colour::Code _colourCode ) = 0;
+ };
+
+ struct NoColourImpl : IColourImpl {
+ void use( Colour::Code ) {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+ return &s_instance;
+ }
+ };
+
+ } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+# ifdef CATCH_PLATFORM_WINDOWS
+# define CATCH_CONFIG_COLOUR_WINDOWS
+# else
+# define CATCH_CONFIG_COLOUR_ANSI
+# endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+ originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+ }
+
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalForegroundAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
+ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
+ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
+ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+ case Colour::Grey: return setTextAttribute( 0 );
+
+ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
+ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+
+ private:
+ void setTextAttribute( WORD _textAttribute ) {
+ SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+ }
+ HANDLE stdoutHandle;
+ WORD originalForegroundAttributes;
+ WORD originalBackgroundAttributes;
+ };
+
+ IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = !isDebuggerActive()
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? &s_instance
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+ // use POSIX/ ANSI console terminal codes
+ // Thanks to Adam Strzelecki for original contribution
+ // (http://github.com/nanoant)
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public IColourImpl {
+ public:
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+ case Colour::Red: return setColour( "[0;31m" );
+ case Colour::Green: return setColour( "[0;32m" );
+ case Colour::Blue: return setColour( "[0;34m" );
+ case Colour::Cyan: return setColour( "[0;36m" );
+ case Colour::Yellow: return setColour( "[0;33m" );
+ case Colour::Grey: return setColour( "[1;30m" );
+
+ case Colour::LightGrey: return setColour( "[0;37m" );
+ case Colour::BrightRed: return setColour( "[1;31m" );
+ case Colour::BrightGreen: return setColour( "[1;32m" );
+ case Colour::BrightWhite: return setColour( "[1;37m" );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+ static IColourImpl* instance() {
+ static PosixColourImpl s_instance;
+ return &s_instance;
+ }
+
+ private:
+ void setColour( const char* _escapeCode ) {
+ Catch::cout() << '\033' << _escapeCode;
+ }
+ };
+
+ IColourImpl* platformColourInstance() {
+ ErrnoGuard guard;
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? PosixColourImpl::instance()
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+ Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+ void Colour::use( Code _colourCode ) {
+ static IColourImpl* impl = platformColourInstance();
+ impl->use( _colourCode );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+ struct GeneratorInfo : IGeneratorInfo {
+
+ GeneratorInfo( std::size_t size )
+ : m_size( size ),
+ m_currentIndex( 0 )
+ {}
+
+ bool moveNext() {
+ if( ++m_currentIndex == m_size ) {
+ m_currentIndex = 0;
+ return false;
+ }
+ return true;
+ }
+
+ std::size_t getCurrentIndex() const {
+ return m_currentIndex;
+ }
+
+ std::size_t m_size;
+ std::size_t m_currentIndex;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class GeneratorsForTest : public IGeneratorsForTest {
+
+ public:
+ ~GeneratorsForTest() {
+ deleteAll( m_generatorsInOrder );
+ }
+
+ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+ std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+ if( it == m_generatorsByName.end() ) {
+ IGeneratorInfo* info = new GeneratorInfo( size );
+ m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+ m_generatorsInOrder.push_back( info );
+ return *info;
+ }
+ return *it->second;
+ }
+
+ bool moveNext() {
+ std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+ std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+ for(; it != itEnd; ++it ) {
+ if( (*it)->moveNext() )
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+ std::vector<IGeneratorInfo*> m_generatorsInOrder;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest()
+ {
+ return new GeneratorsForTest();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+ AssertionInfo::AssertionInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ std::string const& _capturedExpression,
+ ResultDisposition::Flags _resultDisposition )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ capturedExpression( _capturedExpression ),
+ resultDisposition( _resultDisposition )
+ {}
+
+ AssertionResult::AssertionResult() {}
+
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ : m_info( info ),
+ m_resultData( data )
+ {}
+
+ AssertionResult::~AssertionResult() {}
+
+ // Result was a success
+ bool AssertionResult::succeeded() const {
+ return Catch::isOk( m_resultData.resultType );
+ }
+
+ // Result was a success, or failure is suppressed
+ bool AssertionResult::isOk() const {
+ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+ }
+
+ ResultWas::OfType AssertionResult::getResultType() const {
+ return m_resultData.resultType;
+ }
+
+ bool AssertionResult::hasExpression() const {
+ return !m_info.capturedExpression.empty();
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string AssertionResult::getExpression() const {
+ if( isFalseTest( m_info.resultDisposition ) )
+ return '!' + m_info.capturedExpression;
+ else
+ return m_info.capturedExpression;
+ }
+ std::string AssertionResult::getExpressionInMacro() const {
+ if( m_info.macroName.empty() )
+ return m_info.capturedExpression;
+ else
+ return m_info.macroName + "( " + m_info.capturedExpression + " )";
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ return m_resultData.reconstructExpression();
+ }
+
+ std::string AssertionResult::getMessage() const {
+ return m_resultData.message;
+ }
+ SourceLineInfo AssertionResult::getSourceInfo() const {
+ return m_info.lineInfo;
+ }
+
+ std::string AssertionResult::getTestMacroName() const {
+ return m_info.macroName;
+ }
+
+ void AssertionResult::discardDecomposedExpression() const {
+ m_resultData.decomposedExpression = CATCH_NULL;
+ }
+
+ void AssertionResult::expandDecomposedExpression() const {
+ m_resultData.reconstructExpression();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+#include <cctype>
+
+namespace Catch {
+
+ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+ if( startsWith( tag, '.' ) ||
+ tag == "hide" ||
+ tag == "!hide" )
+ return TestCaseInfo::IsHidden;
+ else if( tag == "!throws" )
+ return TestCaseInfo::Throws;
+ else if( tag == "!shouldfail" )
+ return TestCaseInfo::ShouldFail;
+ else if( tag == "!mayfail" )
+ return TestCaseInfo::MayFail;
+ else if( tag == "!nonportable" )
+ return TestCaseInfo::NonPortable;
+ else
+ return TestCaseInfo::None;
+ }
+ inline bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
+ }
+ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ if( isReservedTag( tag ) ) {
+ std::ostringstream ss;
+ ss << Colour(Colour::Red)
+ << "Tag name [" << tag << "] not allowed.\n"
+ << "Tag names starting with non alpha-numeric characters are reserved\n"
+ << Colour(Colour::FileName)
+ << _lineInfo << '\n';
+ throw std::runtime_error(ss.str());
+ }
+ }
+
+ TestCase makeTestCase( ITestCase* _testCase,
+ std::string const& _className,
+ std::string const& _name,
+ std::string const& _descOrTags,
+ SourceLineInfo const& _lineInfo )
+ {
+ bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+ // Parse out tags
+ std::set<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+ char c = _descOrTags[i];
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+ else
+ desc += c;
+ }
+ else {
+ if( c == ']' ) {
+ TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+ if( prop == TestCaseInfo::IsHidden )
+ isHidden = true;
+ else if( prop == TestCaseInfo::None )
+ enforceNotReservedTag( tag, _lineInfo );
+
+ tags.insert( tag );
+ tag.clear();
+ inTag = false;
+ }
+ else
+ tag += c;
+ }
+ }
+ if( isHidden ) {
+ tags.insert( "hide" );
+ tags.insert( "." );
+ }
+
+ TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, info );
+ }
+
+ void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+ {
+ testCaseInfo.tags = tags;
+ testCaseInfo.lcaseTags.clear();
+
+ std::ostringstream oss;
+ for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+ oss << '[' << *it << ']';
+ std::string lcaseTag = toLower( *it );
+ testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+ testCaseInfo.lcaseTags.insert( lcaseTag );
+ }
+ testCaseInfo.tagsAsString = oss.str();
+ }
+
+ TestCaseInfo::TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo )
+ : name( _name ),
+ className( _className ),
+ description( _description ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ setTags( *this, _tags );
+ }
+
+ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+ : name( other.name ),
+ className( other.className ),
+ description( other.description ),
+ tags( other.tags ),
+ lcaseTags( other.lcaseTags ),
+ tagsAsString( other.tagsAsString ),
+ lineInfo( other.lineInfo ),
+ properties( other.properties )
+ {}
+
+ bool TestCaseInfo::isHidden() const {
+ return ( properties & IsHidden ) != 0;
+ }
+ bool TestCaseInfo::throws() const {
+ return ( properties & Throws ) != 0;
+ }
+ bool TestCaseInfo::okToFail() const {
+ return ( properties & (ShouldFail | MayFail ) ) != 0;
+ }
+ bool TestCaseInfo::expectedToFail() const {
+ return ( properties & (ShouldFail ) ) != 0;
+ }
+
+ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+ TestCase::TestCase( TestCase const& other )
+ : TestCaseInfo( other ),
+ test( other.test )
+ {}
+
+ TestCase TestCase::withName( std::string const& _newName ) const {
+ TestCase other( *this );
+ other.name = _newName;
+ return other;
+ }
+
+ void TestCase::swap( TestCase& other ) {
+ test.swap( other.test );
+ name.swap( other.name );
+ className.swap( other.className );
+ description.swap( other.description );
+ tags.swap( other.tags );
+ lcaseTags.swap( other.lcaseTags );
+ tagsAsString.swap( other.tagsAsString );
+ std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+ std::swap( lineInfo, other.lineInfo );
+ }
+
+ void TestCase::invoke() const {
+ test->invoke();
+ }
+
+ bool TestCase::operator == ( TestCase const& other ) const {
+ return test.get() == other.test.get() &&
+ name == other.name &&
+ className == other.className;
+ }
+
+ bool TestCase::operator < ( TestCase const& other ) const {
+ return name < other.name;
+ }
+ TestCase& TestCase::operator = ( TestCase const& other ) {
+ TestCase temp( other );
+ swap( temp );
+ return *this;
+ }
+
+ TestCaseInfo const& TestCase::getTestCaseInfo() const
+ {
+ return *this;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+ Version::Version
+ ( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ patchNumber( _patchNumber ),
+ branchName( _branchName ),
+ buildNumber( _buildNumber )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, Version const& version ) {
+ os << version.majorVersion << '.'
+ << version.minorVersion << '.'
+ << version.patchNumber;
+ // branchName is never null -> 0th char is \0 if it is empty
+ if (version.branchName[0]) {
+ os << '-' << version.branchName
+ << '.' << version.buildNumber;
+ }
+ return os;
+ }
+
+ inline Version libraryVersion() {
+ static Version version( 1, 9, 4, "", 0 );
+ return version;
+ }
+
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+ MessageInfo::MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ type( _type ),
+ sequence( ++globalCount )
+ {}
+
+ // This may need protecting if threading support is added
+ unsigned int MessageInfo::globalCount = 0;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+ : m_info( builder.m_info )
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+ ScopedMessage::ScopedMessage( ScopedMessage const& other )
+ : m_info( other.m_info )
+ {}
+
+ ScopedMessage::~ScopedMessage() {
+ if ( !std::uncaught_exception() ){
+ getResultCapture().popScopedMessage(m_info);
+ }
+ }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+ // Deprecated
+ struct IReporter : IShared {
+ virtual ~IReporter();
+
+ virtual bool shouldRedirectStdout() const = 0;
+
+ virtual void StartTesting() = 0;
+ virtual void EndTesting( Totals const& totals ) = 0;
+ virtual void StartGroup( std::string const& groupName ) = 0;
+ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+ virtual void Aborted() = 0;
+ virtual void Result( AssertionResult const& result ) = 0;
+ };
+
+ class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+ {
+ public:
+ LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+ virtual ~LegacyReporterAdapter();
+
+ virtual ReporterPreferences getPreferences() const;
+ virtual void noMatchingTestCases( std::string const& );
+ virtual void testRunStarting( TestRunInfo const& );
+ virtual void testGroupStarting( GroupInfo const& groupInfo );
+ virtual void testCaseStarting( TestCaseInfo const& testInfo );
+ virtual void sectionStarting( SectionInfo const& sectionInfo );
+ virtual void assertionStarting( AssertionInfo const& );
+ virtual bool assertionEnded( AssertionStats const& assertionStats );
+ virtual void sectionEnded( SectionStats const& sectionStats );
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+ virtual void testRunEnded( TestRunStats const& testRunStats );
+ virtual void skipTest( TestCaseInfo const& );
+
+ private:
+ Ptr<IReporter> m_legacyReporter;
+ };
+}
+
+namespace Catch
+{
+ LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+ : m_legacyReporter( legacyReporter )
+ {}
+ LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+ ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+ return prefs;
+ }
+
+ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+ m_legacyReporter->StartTesting();
+ }
+ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+ m_legacyReporter->StartGroup( groupInfo.name );
+ }
+ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ m_legacyReporter->StartTestCase( testInfo );
+ }
+ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+ }
+ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+ // Not on legacy interface
+ }
+
+ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+ rb << it->message;
+ rb.setResultType( ResultWas::Info );
+ AssertionResult result = rb.build();
+ m_legacyReporter->Result( result );
+ }
+ }
+ }
+ m_legacyReporter->Result( assertionStats.assertionResult );
+ return true;
+ }
+ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+ if( sectionStats.missingAssertions )
+ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+ }
+ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ m_legacyReporter->EndTestCase
+ ( testCaseStats.testInfo,
+ testCaseStats.totals,
+ testCaseStats.stdOut,
+ testCaseStats.stdErr );
+ }
+ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ if( testGroupStats.aborting )
+ m_legacyReporter->Aborted();
+ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+ }
+ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+ m_legacyReporter->EndTesting( testRunStats.totals );
+ }
+ void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
+ }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+#else
+
+#include <sys/time.h>
+
+#endif
+
+namespace Catch {
+
+ namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+ UInt64 getCurrentTicks() {
+ static UInt64 hz=0, hzo=0;
+ if (!hz) {
+ QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+ }
+ UInt64 t;
+ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+ return ((t-hzo)*1000000)/hz;
+ }
+#else
+ UInt64 getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t,CATCH_NULL);
+ return static_cast<UInt64>( t.tv_sec ) * 1000000ull + static_cast<UInt64>( t.tv_usec );
+ }
+#endif
+ }
+
+ void Timer::start() {
+ m_ticks = getCurrentTicks();
+ }
+ unsigned int Timer::getElapsedMicroseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ unsigned int Timer::getElapsedMilliseconds() const {
+ return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+ }
+ double Timer::getElapsedSeconds() const {
+ return getElapsedMicroseconds()/1000000.0;
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+#include <cstring>
+#include <cctype>
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+ }
+ bool startsWith( std::string const& s, char prefix ) {
+ return !s.empty() && s[0] == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+ }
+ bool endsWith( std::string const& s, char suffix ) {
+ return !s.empty() && s[s.size()-1] == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( c ) );
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+ }
+ std::string toLower( std::string const& s ) {
+ std::string lc = s;
+ toLowerInPlace( lc );
+ return lc;
+ }
+ std::string trim( std::string const& str ) {
+ static char const* whitespaceChars = "\n\r\t ";
+ std::string::size_type start = str.find_first_not_of( whitespaceChars );
+ std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+ }
+
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+ bool replaced = false;
+ std::size_t i = str.find( replaceThis );
+ while( i != std::string::npos ) {
+ replaced = true;
+ str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+ if( i < str.size()-withThis.size() )
+ i = str.find( replaceThis, i+withThis.size() );
+ else
+ i = std::string::npos;
+ }
+ return replaced;
+ }
+
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+ os << pluraliser.m_count << ' ' << pluraliser.m_label;
+ if( pluraliser.m_count != 1 )
+ os << 's';
+ return os;
+ }
+
+ SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){}
+ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+ : file( _file ),
+ line( _line )
+ {}
+ bool SourceLineInfo::empty() const {
+ return file[0] == '\0';
+ }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+ }
+ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+ return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
+ }
+
+ void seedRng( IConfig const& config ) {
+ if( config.rngSeed() != 0 )
+ std::srand( config.rngSeed() );
+ }
+ unsigned int rngSeed() {
+ return getCurrentContext().getConfig()->rngSeed();
+ }
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << '(' << info.line << ')';
+#else
+ os << info.file << ':' << info.line;
+#endif
+ return os;
+ }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+ std::ostringstream oss;
+ oss << locationInfo << ": Internal Catch error: '" << message << '\'';
+ if( alwaysTrue() )
+ throw std::logic_error( oss.str() );
+ }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+ SectionInfo::SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description )
+ : name( _name ),
+ description( _description ),
+ lineInfo( _lineInfo )
+ {}
+
+ Section::Section( SectionInfo const& info )
+ : m_info( info ),
+ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+ {
+ m_timer.start();
+ }
+
+ Section::~Section() {
+ if( m_sectionIncluded ) {
+ SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+ if( std::uncaught_exception() )
+ getResultCapture().sectionEndedEarly( endInfo );
+ else
+ getResultCapture().sectionEnded( endInfo );
+ }
+ }
+
+ // This indicates whether the section should be executed or not
+ Section::operator bool() const {
+ return m_sectionIncluded;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#ifdef CATCH_PLATFORM_MAC
+
+ #include <assert.h>
+ #include <stdbool.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/sysctl.h>
+
+ namespace Catch{
+
+ // The following function is taken directly from the following technical note:
+ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
+ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
+ } // namespace Catch
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ #include <fstream>
+ #include <string>
+
+ namespace Catch{
+ // The standard POSIX way of detecting a debugger is to attempt to
+ // ptrace() the process, but this needs to be done from a child and not
+ // this process itself to still allow attaching to this process later
+ // if wanted, so is rather heavy. Under Linux we have the PID of the
+ // "debugger" (which doesn't need to be gdb, of course, it could also
+ // be strace, for example) in /proc/$PID/status, so just get it from
+ // there instead.
+ bool isDebuggerActive(){
+ // Libstdc++ has a bug, where std::ifstream sets errno to 0
+ // This way our users can properly assert over errno values
+ ErrnoGuard guard;
+ std::ifstream in("/proc/self/status");
+ for( std::string line; std::getline(in, line); ) {
+ static const int PREFIX_LEN = 11;
+ if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+ // We're traced if the PID is not 0 and no other PID starts
+ // with 0 digit, so it's enough to check for just a single
+ // character.
+ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+ }
+ }
+
+ return false;
+ }
+ } // namespace Catch
+#elif defined(_MSC_VER)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#else
+ namespace Catch {
+ inline bool isDebuggerActive() { return false; }
+ }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ ::OutputDebugStringA( text.c_str() );
+ }
+ }
+#else
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ // !TBD: Need a version for Mac/ XCode and other IDEs
+ Catch::cout() << text;
+ }
+ }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+ const std::string unprintableString = "{?}";
+
+ namespace {
+ const int hexThreshold = 255;
+
+ struct Endianness {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+ union _{
+ int asInt;
+ char asChar[sizeof (int)];
+ } u;
+
+ u.asInt = 1;
+ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+ }
+ };
+ }
+
+ std::string rawMemoryToString( const void *object, std::size_t size )
+ {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>( size ), inc = 1;
+ if( Endianness::which() == Endianness::Little ) {
+ i = end-1;
+ end = inc = -1;
+ }
+
+ unsigned char const *bytes = static_cast<unsigned char const *>(object);
+ std::ostringstream os;
+ os << "0x" << std::setfill('0') << std::hex;
+ for( ; i != end; i += inc )
+ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return os.str();
+ }
+}
+
+std::string toString( std::string const& value ) {
+ std::string s = value;
+ if( getCurrentContext().getConfig()->showInvisibles() ) {
+ for(size_t i = 0; i < s.size(); ++i ) {
+ std::string subs;
+ switch( s[i] ) {
+ case '\n': subs = "\\n"; break;
+ case '\t': subs = "\\t"; break;
+ default: break;
+ }
+ if( !subs.empty() ) {
+ s = s.substr( 0, i ) + subs + s.substr( i+1 );
+ ++i;
+ }
+ }
+ }
+ return '"' + s + '"';
+}
+std::string toString( std::wstring const& value ) {
+
+ std::string s;
+ s.reserve( value.size() );
+ for(size_t i = 0; i < value.size(); ++i )
+ s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+ return Catch::toString( s );
+}
+
+std::string toString( const char* const value ) {
+ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+ return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+ return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+ return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+ std::ostringstream oss;
+ oss << std::setprecision( precision )
+ << std::fixed
+ << value;
+ std::string d = oss.str();
+ std::size_t i = d.find_last_not_of( '0' );
+ if( i != std::string::npos && i != d.size()-1 ) {
+ if( d[i] == '.' )
+ i++;
+ d = d.substr( 0, i+1 );
+ }
+ return d;
+}
+
+std::string toString( const double value ) {
+ return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+ return fpToString( value, 5 ) + 'f';
+}
+
+std::string toString( bool value ) {
+ return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+ if ( value == '\r' )
+ return "'\\r'";
+ if ( value == '\f' )
+ return "'\\f'";
+ if ( value == '\n' )
+ return "'\\n'";
+ if ( value == '\t' )
+ return "'\\t'";
+ if ( '\0' <= value && value < ' ' )
+ return toString( static_cast<unsigned int>( value ) );
+ char chstr[] = "' '";
+ chstr[1] = value;
+ return chstr;
+}
+
+std::string toString( signed char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+std::string toString( unsigned long long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+ return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSObject* const& nsObject ) {
+ return toString( [nsObject description] );
+ }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+ std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) {
+ return secondArg.empty() || secondArg == "\"\""
+ ? capturedExpression
+ : capturedExpression + ", " + secondArg;
+ }
+ ResultBuilder::ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition,
+ char const* secondArg )
+ : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ),
+ m_shouldDebugBreak( false ),
+ m_shouldThrow( false ),
+ m_guardException( false )
+ {}
+
+ ResultBuilder::~ResultBuilder() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+ if ( m_guardException ) {
+ m_stream.oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+ captureResult( ResultWas::ThrewException );
+ getCurrentContext().getResultCapture()->exceptionEarlyReported();
+ }
+#endif
+ }
+
+ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+ m_data.resultType = result;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setResultType( bool result ) {
+ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+ return *this;
+ }
+
+ void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
+ AssertionResult result = build( expr );
+ handleResult( result );
+ }
+
+ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+ m_assertionInfo.resultDisposition = resultDisposition;
+ m_stream.oss << Catch::translateActiveException();
+ captureResult( ResultWas::ThrewException );
+ }
+
+ void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+ setResultType( resultType );
+ captureExpression();
+ }
+
+ void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+ if( expectedMessage.empty() )
+ captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
+ else
+ captureExpectedException( Matchers::Equals( expectedMessage ) );
+ }
+
+ void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
+
+ assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
+ AssertionResultData data = m_data;
+ data.resultType = ResultWas::Ok;
+ data.reconstructedExpression = m_assertionInfo.capturedExpression;
+
+ std::string actualMessage = Catch::translateActiveException();
+ if( !matcher.match( actualMessage ) ) {
+ data.resultType = ResultWas::ExpressionFailed;
+ data.reconstructedExpression = actualMessage;
+ }
+ AssertionResult result( m_assertionInfo, data );
+ handleResult( result );
+ }
+
+ void ResultBuilder::captureExpression() {
+ AssertionResult result = build();
+ handleResult( result );
+ }
+
+ void ResultBuilder::handleResult( AssertionResult const& result )
+ {
+ getResultCapture().assertionEnded( result );
+
+ if( !result.isOk() ) {
+ if( getCurrentContext().getConfig()->shouldDebugBreak() )
+ m_shouldDebugBreak = true;
+ if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+ m_shouldThrow = true;
+ }
+ }
+
+ void ResultBuilder::react() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+ if (m_shouldDebugBreak) {
+ ///////////////////////////////////////////////////////////////////
+ // To inspect the state during test, you need to go one level up the callstack
+ // To go back to the test and change execution, jump over the throw statement
+ ///////////////////////////////////////////////////////////////////
+ CATCH_BREAK_INTO_DEBUGGER();
+ }
+#endif
+ if( m_shouldThrow )
+ throw Catch::TestFailureException();
+ }
+
+ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+ AssertionResult ResultBuilder::build() const
+ {
+ return build( *this );
+ }
+
+ // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
+ // a temporary DecomposedExpression, which in turn holds references to
+ // operands, possibly temporary as well.
+ // It should immediately be passed to handleResult; if the expression
+ // needs to be reported, its string expansion must be composed before
+ // the temporaries are destroyed.
+ AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
+ AssertionResultData data = m_data;
+
+ // Flip bool results if FalseTest flag is set
+ if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
+ data.negate( expr.isBinaryExpression() );
+ }
+
+ data.message = m_stream.oss.str();
+ data.decomposedExpression = &expr; // for lazy reconstruction
+ return AssertionResult( m_assertionInfo, data );
+ }
+
+ void ResultBuilder::reconstructExpression( std::string& dest ) const {
+ dest = m_assertionInfo.capturedExpression;
+ }
+
+ void ResultBuilder::setExceptionGuard() {
+ m_guardException = true;
+ }
+ void ResultBuilder::unsetExceptionGuard() {
+ m_guardException = false;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+namespace Catch {
+
+ TagAliasRegistry::~TagAliasRegistry() {}
+
+ Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+ std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+ if( it != m_registry.end() )
+ return it->second;
+ else
+ return Option<TagAlias>();
+ }
+
+ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+ std::string expandedTestSpec = unexpandedTestSpec;
+ for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+ it != itEnd;
+ ++it ) {
+ std::size_t pos = expandedTestSpec.find( it->first );
+ if( pos != std::string::npos ) {
+ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
+ it->second.tag +
+ expandedTestSpec.substr( pos + it->first.size() );
+ }
+ }
+ return expandedTestSpec;
+ }
+
+ void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
+ std::ostringstream oss;
+ oss << Colour( Colour::Red )
+ << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n"
+ << Colour( Colour::FileName )
+ << lineInfo << '\n';
+ throw std::domain_error( oss.str().c_str() );
+ }
+ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+ std::ostringstream oss;
+ oss << Colour( Colour::Red )
+ << "error: tag alias, \"" << alias << "\" already registered.\n"
+ << "\tFirst seen at "
+ << Colour( Colour::Red ) << find(alias)->lineInfo << '\n'
+ << Colour( Colour::Red ) << "\tRedefined at "
+ << Colour( Colour::FileName) << lineInfo << '\n';
+ throw std::domain_error( oss.str().c_str() );
+ }
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+
+ ITagAliasRegistry const& ITagAliasRegistry::get() {
+ return getRegistryHub().getTagAliasRegistry();
+ }
+
+ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+ getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_str( adjustString( str ) )
+ {}
+ std::string CasedString::adjustString( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? toLower( str )
+ : str;
+ }
+ std::string CasedString::caseSensitivitySuffix() const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? " (case insensitive)"
+ : std::string();
+ }
+
+ StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+ : m_comparator( comparator ),
+ m_operation( operation ) {
+ }
+
+ std::string StringMatcherBase::describe() const {
+ std::string description;
+ description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+ m_comparator.caseSensitivitySuffix().size());
+ description += m_operation;
+ description += ": \"";
+ description += m_comparator.m_str;
+ description += "\"";
+ description += m_comparator.caseSensitivitySuffix();
+ return description;
+ }
+
+ EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+ bool EqualsMatcher::match( std::string const& source ) const {
+ return m_comparator.adjustString( source ) == m_comparator.m_str;
+ }
+
+ ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+ bool ContainsMatcher::match( std::string const& source ) const {
+ return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+ bool StartsWithMatcher::match( std::string const& source ) const {
+ return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+ bool EndsWithMatcher::match( std::string const& source ) const {
+ return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ } // namespace StdString
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+ typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+ Reporters m_reporters;
+
+public:
+ void add( Ptr<IStreamingReporter> const& reporter ) {
+ m_reporters.push_back( reporter );
+ }
+
+public: // IStreamingReporter
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporters[0]->getPreferences();
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->noMatchingTestCases( spec );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testRunStarting( testRunInfo );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testGroupStarting( groupInfo );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testCaseStarting( testInfo );
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->sectionStarting( sectionInfo );
+ }
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->assertionStarting( assertionInfo );
+ }
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ bool clearBuffer = false;
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ clearBuffer |= (*it)->assertionEnded( assertionStats );
+ return clearBuffer;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->sectionEnded( sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testGroupEnded( testGroupStats );
+ }
+
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testRunEnded( testRunStats );
+ }
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->skipTest( testInfo );
+ }
+
+ virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+ return this;
+ }
+
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+ Ptr<IStreamingReporter> resultingReporter;
+
+ if( existingReporter ) {
+ MultipleReporters* multi = existingReporter->tryAsMulti();
+ if( !multi ) {
+ multi = new MultipleReporters;
+ resultingReporter = Ptr<IStreamingReporter>( multi );
+ if( existingReporter )
+ multi->add( existingReporter );
+ }
+ else
+ resultingReporter = existingReporter;
+ multi->add( additionalReporter );
+ }
+ else
+ resultingReporter = additionalReporter;
+
+ return resultingReporter;
+}
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <assert.h>
+
+namespace Catch {
+
+ namespace {
+ // Because formatting using c++ streams is stateful, drop down to C is required
+ // Alternatively we could use stringstream, but its performance is... not good.
+ std::string getFormattedDuration( double duration ) {
+ // Max exponent + 1 is required to represent the whole part
+ // + 1 for decimal point
+ // + 3 for the 3 decimal places
+ // + 1 for null terminator
+ const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+ char buffer[maxDoubleSize];
+
+ // Save previous errno, to prevent sprintf from overwriting it
+ ErrnoGuard guard;
+#ifdef _MSC_VER
+ sprintf_s(buffer, "%.3f", duration);
+#else
+ sprintf(buffer, "%.3f", duration);
+#endif
+ return std::string(buffer);
+ }
+ }
+
+ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ }
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporterPrefs;
+ }
+
+ virtual ~StreamingReporterBase() CATCH_OVERRIDE;
+
+ virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
+
+ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
+ currentTestRunInfo = _testRunInfo;
+ }
+ virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
+ currentGroupInfo = _groupInfo;
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
+ currentTestCaseInfo = _testInfo;
+ }
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+ m_sectionStack.push_back( _sectionInfo );
+ }
+
+ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
+ currentTestCaseInfo.reset();
+ }
+ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
+ currentGroupInfo.reset();
+ }
+ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
+ // Don't do anything with this by default.
+ // It can optionally be overridden in the derived class.
+ }
+
+ Ptr<IConfig const> m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+ };
+
+ struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+ template<typename T, typename ChildNodeT>
+ struct Node : SharedImpl<> {
+ explicit Node( T const& _value ) : value( _value ) {}
+ virtual ~Node() {}
+
+ typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+ T value;
+ ChildNodes children;
+ };
+ struct SectionNode : SharedImpl<> {
+ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+ virtual ~SectionNode();
+
+ bool operator == ( SectionNode const& other ) const {
+ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+ }
+ bool operator == ( Ptr<SectionNode> const& other ) const {
+ return operator==( *other );
+ }
+
+ SectionStats stats;
+ typedef std::vector<Ptr<SectionNode> > ChildSections;
+ typedef std::vector<AssertionStats> Assertions;
+ ChildSections childSections;
+ Assertions assertions;
+ std::string stdOut;
+ std::string stdErr;
+ };
+
+ struct BySectionInfo {
+ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ bool operator() ( Ptr<SectionNode> const& node ) const {
+ return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+ }
+ private:
+ void operator=( BySectionInfo const& );
+ SectionInfo const& m_other;
+ };
+
+ typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+ typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+ typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+ CumulativeReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ }
+ ~CumulativeReporterBase();
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporterPrefs;
+ }
+
+ virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+ virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ Ptr<SectionNode> node;
+ if( m_sectionStack.empty() ) {
+ if( !m_rootSection )
+ m_rootSection = new SectionNode( incompleteStats );
+ node = m_rootSection;
+ }
+ else {
+ SectionNode& parentNode = *m_sectionStack.back();
+ SectionNode::ChildSections::const_iterator it =
+ std::find_if( parentNode.childSections.begin(),
+ parentNode.childSections.end(),
+ BySectionInfo( sectionInfo ) );
+ if( it == parentNode.childSections.end() ) {
+ node = new SectionNode( incompleteStats );
+ parentNode.childSections.push_back( node );
+ }
+ else
+ node = *it;
+ }
+ m_sectionStack.push_back( node );
+ m_deepestSection = node;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ assert( !m_sectionStack.empty() );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back( assertionStats );
+ // AssertionResult holds a pointer to a temporary DecomposedExpression,
+ // which getExpandedExpression() calls to build the expression string.
+ // Our section stack copy of the assertionResult will likely outlive the
+ // temporary, so it must be expanded or discarded now to avoid calling
+ // a destroyed object later.
+ prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
+ return true;
+ }
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ assert( !m_sectionStack.empty() );
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+ assert( m_sectionStack.size() == 0 );
+ node->children.push_back( m_rootSection );
+ m_testCases.push_back( node );
+ m_rootSection.reset();
+
+ assert( m_deepestSection );
+ m_deepestSection->stdOut = testCaseStats.stdOut;
+ m_deepestSection->stdErr = testCaseStats.stdErr;
+ }
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+ node->children.swap( m_testCases );
+ m_testGroups.push_back( node );
+ }
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+ node->children.swap( m_testGroups );
+ m_testRuns.push_back( node );
+ testRunEndedCumulative();
+ }
+ virtual void testRunEndedCumulative() = 0;
+
+ virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void prepareExpandedExpression( AssertionResult& result ) const {
+ if( result.isOk() )
+ result.discardDecomposedExpression();
+ else
+ result.expandDecomposedExpression();
+ }
+
+ Ptr<IConfig const> m_config;
+ std::ostream& stream;
+ std::vector<AssertionStats> m_assertions;
+ std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+ std::vector<Ptr<TestCaseNode> > m_testCases;
+ std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+ std::vector<Ptr<TestRunNode> > m_testRuns;
+
+ Ptr<SectionNode> m_rootSection;
+ Ptr<SectionNode> m_deepestSection;
+ std::vector<Ptr<SectionNode> > m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+
+ };
+
+ template<char C>
+ char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+ struct TestEventListenerBase : StreamingReporterBase {
+ TestEventListenerBase( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+ virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+ return false;
+ }
+ };
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+ template<typename T>
+ class LegacyReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new LegacyReporterAdapter( new T( config ) );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ LegacyReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ReporterRegistrar {
+
+ class ReporterFactory : public SharedImpl<IReporterFactory> {
+
+ // *** Please Note ***:
+ // - If you end up here looking at a compiler error because it's trying to register
+ // your custom reporter class be aware that the native reporter interface has changed
+ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+ // However please consider updating to the new interface as the old one is now
+ // deprecated and will probably be removed quite soon!
+ // Please contact me via github if you have any questions at all about this.
+ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+ // no idea who is actually using custom reporters at all (possibly no-one!).
+ // The new interface is designed to minimise exposure to interface changes in the future.
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ ReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ListenerRegistrar {
+
+ class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+ virtual std::string getDescription() const {
+ return std::string();
+ }
+ };
+
+ public:
+
+ ListenerRegistrar() {
+ getMutableRegistryHub().registerListener( new ListenerFactory() );
+ }
+ };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+ namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// Deprecated - use the form without INTERNAL_
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+
+namespace Catch {
+
+ class XmlEncode {
+ public:
+ enum ForWhat { ForTextNodes, ForAttributes };
+
+ XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+ : m_str( str ),
+ m_forWhat( forWhat )
+ {}
+
+ void encodeTo( std::ostream& os ) const {
+
+ // Apostrophe escaping not necessary if we always use " to write attributes
+ // (see: http://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+ char c = m_str[i];
+ switch( c ) {
+ case '<': os << "&lt;"; break;
+ case '&': os << "&amp;"; break;
+
+ case '>':
+ // See: http://www.w3.org/TR/xml/#syntax
+ if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+ os << "&gt;";
+ else
+ os << c;
+ break;
+
+ case '\"':
+ if( m_forWhat == ForAttributes )
+ os << "&quot;";
+ else
+ os << c;
+ break;
+
+ default:
+ // Escape control chars - based on contribution by @espenalb in PR #465 and
+ // by @mrpi PR #588
+ if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
+ // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>( c );
+ }
+ else
+ os << c;
+ }
+ }
+ }
+
+ friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+ xmlEncode.encodeTo( os );
+ return os;
+ }
+
+ private:
+ std::string m_str;
+ ForWhat m_forWhat;
+ };
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ ScopedElement( ScopedElement const& other )
+ : m_writer( other.m_writer ){
+ other.m_writer = CATCH_NULL;
+ }
+
+ ~ScopedElement() {
+ if( m_writer )
+ m_writer->endElement();
+ }
+
+ ScopedElement& writeText( std::string const& text, bool indent = true ) {
+ m_writer->writeText( text, indent );
+ return *this;
+ }
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer;
+ };
+
+ XmlWriter()
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( Catch::cout() )
+ {
+ writeDeclaration();
+ }
+
+ XmlWriter( std::ostream& os )
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( os )
+ {
+ writeDeclaration();
+ }
+
+ ~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+ XmlWriter& startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ m_os << m_indent << '<' << name;
+ m_tags.push_back( name );
+ m_indent += " ";
+ m_tagIsOpen = true;
+ return *this;
+ }
+
+ ScopedElement scopedElement( std::string const& name ) {
+ ScopedElement scoped( this );
+ startElement( name );
+ return scoped;
+ }
+
+ XmlWriter& endElement() {
+ newlineIfNecessary();
+ m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ if( m_tagIsOpen ) {
+ m_os << "/>";
+ m_tagIsOpen = false;
+ }
+ else {
+ m_os << m_indent << "</" << m_tags.back() << ">";
+ }
+ m_os << std::endl;
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() )
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+ m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+ return *this;
+ }
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ std::ostringstream oss;
+ oss << attribute;
+ return writeAttribute( name, oss.str() );
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ m_os << m_indent;
+ m_os << XmlEncode( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ XmlWriter& writeComment( std::string const& text ) {
+ ensureTagClosed();
+ m_os << m_indent << "<!--" << text << "-->";
+ m_needsNewline = true;
+ return *this;
+ }
+
+ void writeStylesheetRef( std::string const& url ) {
+ m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+ }
+
+ XmlWriter& writeBlankLine() {
+ ensureTagClosed();
+ m_os << '\n';
+ return *this;
+ }
+
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ m_os << ">" << std::endl;
+ m_tagIsOpen = false;
+ }
+ }
+
+ private:
+ XmlWriter( XmlWriter const& );
+ void operator=( XmlWriter const& );
+
+ void writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ }
+
+ void newlineIfNecessary() {
+ if( m_needsNewline ) {
+ m_os << std::endl;
+ m_needsNewline = false;
+ }
+ }
+
+ bool m_tagIsOpen;
+ bool m_needsNewline;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream& m_os;
+ };
+
+}
+// #included from: catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(pop)
+# else
+# pragma clang diagnostic pop
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+
+namespace Catch {
+ class XmlReporter : public StreamingReporterBase {
+ public:
+ XmlReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_xml(_config.stream()),
+ m_sectionDepth( 0 )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ }
+
+ virtual ~XmlReporter() CATCH_OVERRIDE;
+
+ static std::string getDescription() {
+ return "Reports test results as an XML document";
+ }
+
+ virtual std::string getStylesheetRef() const {
+ return std::string();
+ }
+
+ void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+ m_xml
+ .writeAttribute( "filename", sourceInfo.file )
+ .writeAttribute( "line", sourceInfo.line );
+ }
+
+ public: // StreamingReporterBase
+
+ virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
+ StreamingReporterBase::noMatchingTestCases( s );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testRunStarting( testInfo );
+ std::string stylesheetRef = getStylesheetRef();
+ if( !stylesheetRef.empty() )
+ m_xml.writeStylesheetRef( stylesheetRef );
+ m_xml.startElement( "Catch" );
+ if( !m_config->name().empty() )
+ m_xml.writeAttribute( "name", m_config->name() );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testGroupStarting( groupInfo );
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupInfo.name );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseStarting(testInfo);
+ m_xml.startElement( "TestCase" )
+ .writeAttribute( "name", trim( testInfo.name ) )
+ .writeAttribute( "description", testInfo.description )
+ .writeAttribute( "tags", testInfo.tagsAsString );
+
+ writeSourceInfo( testInfo.lineInfo );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ m_testCaseTimer.start();
+ m_xml.ensureTagClosed();
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::sectionStarting( sectionInfo );
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionInfo.name ) )
+ .writeAttribute( "description", sectionInfo.description );
+ writeSourceInfo( sectionInfo.lineInfo );
+ m_xml.ensureTagClosed();
+ }
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+
+ AssertionResult const& result = assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ if( includeResults ) {
+ // Print any info messages in <Info> tags.
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ m_xml.scopedElement( "Info" )
+ .writeText( it->message );
+ } else if ( it->type == ResultWas::Warning ) {
+ m_xml.scopedElement( "Warning" )
+ .writeText( it->message );
+ }
+ }
+ }
+
+ // Drop out if result was successful but we're not printing them.
+ if( !includeResults && result.getResultType() != ResultWas::Warning )
+ return true;
+
+ // Print the expression if there is one.
+ if( result.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", result.succeeded() )
+ .writeAttribute( "type", result.getTestMacroName() );
+
+ writeSourceInfo( result.getSourceInfo() );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( result.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( result.getExpandedExpression() );
+ }
+
+ // And... Print a result applicable to each result type.
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.startElement( "Exception" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::FatalErrorCondition:
+ m_xml.startElement( "FatalErrorCondition" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( result.getMessage() );
+ break;
+ case ResultWas::Warning:
+ // Warning will already have been written
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.startElement( "Failure" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ default:
+ break;
+ }
+
+ if( result.hasExpression() )
+ m_xml.endElement();
+
+ return true;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::sectionEnded( sectionStats );
+ if( --m_sectionDepth > 0 ) {
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+ e.writeAttribute( "successes", sectionStats.assertions.passed );
+ e.writeAttribute( "failures", sectionStats.assertions.failed );
+ e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+ m_xml.endElement();
+ }
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseEnded( testCaseStats );
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+ e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+ if( !testCaseStats.stdOut.empty() )
+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+ if( !testCaseStats.stdErr.empty() )
+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+
+ m_xml.endElement();
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testGroupEnded( testGroupStats );
+ // TODO: Check testGroupStats.aborting and act accordingly.
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+ .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testRunEnded( testRunStats );
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+ .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ private:
+ Timer m_testCaseTimer;
+ XmlWriter m_xml;
+ int m_sectionDepth;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+ namespace {
+ std::string getCurrentTimestamp() {
+ // Beware, this is not reentrant because of backward compatibility issues
+ // Also, UTC only, again because of backward compatibility (%z is C++11)
+ time_t rawtime;
+ std::time(&rawtime);
+ const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &rawtime);
+#else
+ std::tm* timeInfo;
+ timeInfo = std::gmtime(&rawtime);
+#endif
+
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp);
+ }
+
+ }
+
+ class JunitReporter : public CumulativeReporterBase {
+ public:
+ JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() ),
+ m_okToFail( false )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ }
+
+ virtual ~JunitReporter() CATCH_OVERRIDE;
+
+ static std::string getDescription() {
+ return "Reports test results in an XML format that looks like Ant's junitreport target";
+ }
+
+ virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
+
+ virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ suiteTimer.start();
+ stdOutForSuite.str("");
+ stdErrForSuite.str("");
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE {
+ m_okToFail = testCaseInfo.okToFail();
+ }
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ stdOutForSuite << testCaseStats.stdOut;
+ stdErrForSuite << testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ virtual void testRunEndedCumulative() CATCH_OVERRIDE {
+ xml.endElement();
+ }
+
+ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests", stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+ xml.writeAttribute( "time", suiteTime );
+ xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
+ // Write test cases
+ for( TestGroupNode::ChildNodes::const_iterator
+ it = groupNode.children.begin(), itEnd = groupNode.children.end();
+ it != itEnd;
+ ++it )
+ writeTestCase( **it );
+
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+ }
+
+ void writeTestCase( TestCaseNode const& testCaseNode ) {
+ TestCaseStats const& stats = testCaseNode.value;
+
+ // All test cases have exactly one section - which represents the
+ // test case itself. That section may have 0-n nested sections
+ assert( testCaseNode.children.size() == 1 );
+ SectionNode const& rootSection = *testCaseNode.children.front();
+
+ std::string className = stats.testInfo.className;
+
+ if( className.empty() ) {
+ if( rootSection.childSections.empty() )
+ className = "global";
+ }
+ writeSection( className, "", rootSection );
+ }
+
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode ) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + '/' + name;
+
+ if( !sectionNode.assertions.empty() ||
+ !sectionNode.stdOut.empty() ||
+ !sectionNode.stdErr.empty() ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+ if( className.empty() ) {
+ xml.writeAttribute( "classname", name );
+ xml.writeAttribute( "name", "root" );
+ }
+ else {
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+ xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+ if( !sectionNode.stdErr.empty() )
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+ }
+ for( SectionNode::ChildSections::const_iterator
+ it = sectionNode.childSections.begin(),
+ itEnd = sectionNode.childSections.end();
+ it != itEnd;
+ ++it )
+ if( className.empty() )
+ writeSection( name, "", **it );
+ else
+ writeSection( className, name, **it );
+ }
+
+ void writeAssertions( SectionNode const& sectionNode ) {
+ for( SectionNode::Assertions::const_iterator
+ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+ it != itEnd;
+ ++it )
+ writeAssertion( *it );
+ }
+ void writeAssertion( AssertionStats const& stats ) {
+ AssertionResult const& result = stats.assertionResult;
+ if( !result.isOk() ) {
+ std::string elementName;
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ case ResultWas::FatalErrorCondition:
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+ elementName = "failure";
+ break;
+ case ResultWas::ExpressionFailed:
+ elementName = "failure";
+ break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+
+ // We should never see these here:
+ case ResultWas::Info:
+ case ResultWas::Warning:
+ case ResultWas::Ok:
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ elementName = "internalError";
+ break;
+ }
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+ xml.writeAttribute( "message", result.getExpandedExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ std::ostringstream oss;
+ if( !result.getMessage().empty() )
+ oss << result.getMessage() << '\n';
+ for( std::vector<MessageInfo>::const_iterator
+ it = stats.infoMessages.begin(),
+ itEnd = stats.infoMessages.end();
+ it != itEnd;
+ ++it )
+ if( it->type == ResultWas::Info )
+ oss << it->message << '\n';
+
+ oss << "at " << result.getSourceInfo();
+ xml.writeText( oss.str(), false );
+ }
+ }
+
+ XmlWriter xml;
+ Timer suiteTimer;
+ std::ostringstream stdOutForSuite;
+ std::ostringstream stdErrForSuite;
+ unsigned int unexpectedExceptions;
+ bool m_okToFail;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cfloat>
+#include <cstdio>
+
+namespace Catch {
+
+ struct ConsoleReporter : StreamingReporterBase {
+ ConsoleReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_headerPrinted( false )
+ {}
+
+ virtual ~ConsoleReporter() CATCH_OVERRIDE;
+ static std::string getDescription() {
+ return "Reports test results as plain lines of text";
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ // Drop out if result was successful but we're not printing them.
+ if( !includeResults && result.getResultType() != ResultWas::Warning )
+ return false;
+
+ lazyPrint();
+
+ AssertionPrinter printer( stream, _assertionStats, includeResults );
+ printer.print();
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting( _sectionInfo );
+ }
+ virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
+ if( _sectionStats.missingAssertions ) {
+ lazyPrint();
+ Colour colour( Colour::ResultError );
+ if( m_sectionStack.size() > 1 )
+ stream << "\nNo assertions in section";
+ else
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+ if( m_config->showDurations() == ShowDurations::Always ) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ if( m_headerPrinted ) {
+ m_headerPrinted = false;
+ }
+ StreamingReporterBase::sectionEnded( _sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseEnded( _testCaseStats );
+ m_headerPrinted = false;
+ }
+ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
+ if( currentGroupInfo.used ) {
+ printSummaryDivider();
+ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+ printTotals( _testGroupStats.totals );
+ stream << '\n' << std::endl;
+ }
+ StreamingReporterBase::testGroupEnded( _testGroupStats );
+ }
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
+ printTotalsDivider( _testRunStats.totals );
+ printTotals( _testRunStats.totals );
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream ),
+ stats( _stats ),
+ result( _stats.assertionResult ),
+ colour( Colour::None ),
+ message( result.getMessage() ),
+ messages( _stats.infoMessages ),
+ printInfoMessages( _printInfoMessages )
+ {
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ colour = Colour::Success;
+ passOrFail = "PASSED";
+ //if( result.hasMessage() )
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() ) {
+ colour = Colour::Success;
+ passOrFail = "FAILED - but was ok";
+ }
+ else {
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ }
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ThrewException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to unexpected exception with ";
+ if (_stats.infoMessages.size() == 1)
+ messageLabel += "message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel += "messages";
+ break;
+ case ResultWas::FatalErrorCondition:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to a fatal error condition";
+ break;
+ case ResultWas::DidntThrowException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "because no exception was thrown where one was expected";
+ break;
+ case ResultWas::Info:
+ messageLabel = "info";
+ break;
+ case ResultWas::Warning:
+ messageLabel = "warning";
+ break;
+ case ResultWas::ExplicitFailure:
+ passOrFail = "FAILED";
+ colour = Colour::Error;
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "explicitly with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "explicitly with messages";
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ passOrFail = "** internal error **";
+ colour = Colour::Error;
+ break;
+ }
+ }
+
+ void print() const {
+ printSourceInfo();
+ if( stats.totals.assertions.total() > 0 ) {
+ if( result.isOk() )
+ stream << '\n';
+ printResultType();
+ printOriginalExpression();
+ printReconstructedExpression();
+ }
+ else {
+ stream << '\n';
+ }
+ printMessage();
+ }
+
+ private:
+ void printResultType() const {
+ if( !passOrFail.empty() ) {
+ Colour colourGuard( colour );
+ stream << passOrFail << ":\n";
+ }
+ }
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ Colour colourGuard( Colour::OriginalExpression );
+ stream << " ";
+ stream << result.getExpressionInMacro();
+ stream << '\n';
+ }
+ }
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ stream << "with expansion:\n";
+ Colour colourGuard( Colour::ReconstructedExpression );
+ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n';
+ }
+ }
+ void printMessage() const {
+ if( !messageLabel.empty() )
+ stream << messageLabel << ':' << '\n';
+ for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+ it != itEnd;
+ ++it ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || it->type != ResultWas::Info )
+ stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n';
+ }
+ }
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ": ";
+ }
+
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ Colour::Code colour;
+ std::string passOrFail;
+ std::string messageLabel;
+ std::string message;
+ std::vector<MessageInfo> messages;
+ bool printInfoMessages;
+ };
+
+ void lazyPrint() {
+
+ if( !currentTestRunInfo.used )
+ lazyPrintRunInfo();
+ if( !currentGroupInfo.used )
+ lazyPrintGroupInfo();
+
+ if( !m_headerPrinted ) {
+ printTestCaseAndSectionHeader();
+ m_headerPrinted = true;
+ }
+ }
+ void lazyPrintRunInfo() {
+ stream << '\n' << getLineOfChars<'~'>() << '\n';
+ Colour colour( Colour::SecondaryText );
+ stream << currentTestRunInfo->name
+ << " is a Catch v" << libraryVersion() << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ if( m_config->rngSeed() != 0 )
+ stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+ currentTestRunInfo.used = true;
+ }
+ void lazyPrintGroupInfo() {
+ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+ printClosedHeader( "Group: " + currentGroupInfo->name );
+ currentGroupInfo.used = true;
+ }
+ }
+ void printTestCaseAndSectionHeader() {
+ assert( !m_sectionStack.empty() );
+ printOpenHeader( currentTestCaseInfo->name );
+
+ if( m_sectionStack.size() > 1 ) {
+ Colour colourGuard( Colour::Headers );
+
+ std::vector<SectionInfo>::const_iterator
+ it = m_sectionStack.begin()+1, // Skip first section (test case)
+ itEnd = m_sectionStack.end();
+ for( ; it != itEnd; ++it )
+ printHeaderString( it->name, 2 );
+ }
+
+ SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+ if( !lineInfo.empty() ){
+ stream << getLineOfChars<'-'>() << '\n';
+ Colour colourGuard( Colour::FileName );
+ stream << lineInfo << '\n';
+ }
+ stream << getLineOfChars<'.'>() << '\n' << std::endl;
+ }
+
+ void printClosedHeader( std::string const& _name ) {
+ printOpenHeader( _name );
+ stream << getLineOfChars<'.'>() << '\n';
+ }
+ void printOpenHeader( std::string const& _name ) {
+ stream << getLineOfChars<'-'>() << '\n';
+ {
+ Colour colourGuard( Colour::Headers );
+ printHeaderString( _name );
+ }
+ }
+
+ // if string has a : in first line will set indent to follow it on
+ // subsequent lines
+ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+ std::size_t i = _string.find( ": " );
+ if( i != std::string::npos )
+ i+=2;
+ else
+ i = 0;
+ stream << Text( _string, TextAttributes()
+ .setIndent( indent+i)
+ .setInitialIndent( indent ) ) << '\n';
+ }
+
+ struct SummaryColumn {
+
+ SummaryColumn( std::string const& _label, Colour::Code _colour )
+ : label( _label ),
+ colour( _colour )
+ {}
+ SummaryColumn addRow( std::size_t count ) {
+ std::ostringstream oss;
+ oss << count;
+ std::string row = oss.str();
+ for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+ while( it->size() < row.size() )
+ *it = ' ' + *it;
+ while( it->size() > row.size() )
+ row = ' ' + row;
+ }
+ rows.push_back( row );
+ return *this;
+ }
+
+ std::string label;
+ Colour::Code colour;
+ std::vector<std::string> rows;
+
+ };
+
+ void printTotals( Totals const& totals ) {
+ if( totals.testCases.total() == 0 ) {
+ stream << Colour( Colour::Warning ) << "No tests ran\n";
+ }
+ else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
+ stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+ stream << " ("
+ << pluralise( totals.assertions.passed, "assertion" ) << " in "
+ << pluralise( totals.testCases.passed, "test case" ) << ')'
+ << '\n';
+ }
+ else {
+
+ std::vector<SummaryColumn> columns;
+ columns.push_back( SummaryColumn( "", Colour::None )
+ .addRow( totals.testCases.total() )
+ .addRow( totals.assertions.total() ) );
+ columns.push_back( SummaryColumn( "passed", Colour::Success )
+ .addRow( totals.testCases.passed )
+ .addRow( totals.assertions.passed ) );
+ columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+ .addRow( totals.testCases.failed )
+ .addRow( totals.assertions.failed ) );
+ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+ .addRow( totals.testCases.failedButOk )
+ .addRow( totals.assertions.failedButOk ) );
+
+ printSummaryRow( "test cases", columns, 0 );
+ printSummaryRow( "assertions", columns, 1 );
+ }
+ }
+ void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+ for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+ std::string value = it->rows[row];
+ if( it->label.empty() ) {
+ stream << label << ": ";
+ if( value != "0" )
+ stream << value;
+ else
+ stream << Colour( Colour::Warning ) << "- none -";
+ }
+ else if( value != "0" ) {
+ stream << Colour( Colour::LightGrey ) << " | ";
+ stream << Colour( it->colour )
+ << value << ' ' << it->label;
+ }
+ }
+ stream << '\n';
+ }
+
+ static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+ return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+ }
+ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+ if( i > j && i > k )
+ return i;
+ else if( j > k )
+ return j;
+ else
+ return k;
+ }
+
+ void printTotalsDivider( Totals const& totals ) {
+ if( totals.testCases.total() > 0 ) {
+ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )++;
+ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+ if( totals.testCases.allPassed() )
+ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+ else
+ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+ }
+ else {
+ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+ }
+ stream << '\n';
+ }
+ void printSummaryDivider() {
+ stream << getLineOfChars<'-'>() << '\n';
+ }
+
+ private:
+ bool m_headerPrinted;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+ struct CompactReporter : StreamingReporterBase {
+
+ CompactReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual ~CompactReporter();
+
+ static std::string getDescription() {
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {}
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE {
+ if (m_config->showDurations() == ShowDurations::Always) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ }
+
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotals( _testRunStats.totals );
+ stream << '\n' << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream )
+ , stats( _stats )
+ , result( _stats.assertionResult )
+ , messages( _stats.infoMessages )
+ , itMessage( _stats.infoMessages.begin() )
+ , printInfoMessages( _printInfoMessages )
+ {}
+
+ void print() {
+ printSourceInfo();
+
+ itMessage = messages.begin();
+
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ printResultType( Colour::ResultSuccess, passedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ if ( ! result.hasExpression() )
+ printRemainingMessages( Colour::None );
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() )
+ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+ else
+ printResultType( Colour::Error, failedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "unexpected exception with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::FatalErrorCondition:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "fatal error condition with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "expected exception, got none" );
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType( Colour::None, "info" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType( Colour::None, "warning" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "explicitly" );
+ printRemainingMessages( Colour::None );
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ printResultType( Colour::Error, "** internal error **" );
+ break;
+ }
+ }
+
+ private:
+ // Colour::LightGrey
+
+ static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+ static const char* failedString() { return "FAILED"; }
+ static const char* passedString() { return "PASSED"; }
+#else
+ static const char* failedString() { return "failed"; }
+ static const char* passedString() { return "passed"; }
+#endif
+
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ':';
+ }
+
+ void printResultType( Colour::Code colour, std::string const& passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ {
+ Colour colourGuard( colour );
+ stream << ' ' << passOrFail;
+ }
+ stream << ':';
+ }
+ }
+
+ void printIssue( std::string const& issue ) const {
+ stream << ' ' << issue;
+ }
+
+ void printExpressionWas() {
+ if( result.hasExpression() ) {
+ stream << ';';
+ {
+ Colour colour( dimColour() );
+ stream << " expression was:";
+ }
+ printOriginalExpression();
+ }
+ }
+
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ stream << ' ' << result.getExpression();
+ }
+ }
+
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ {
+ Colour colour( dimColour() );
+ stream << " for: ";
+ }
+ stream << result.getExpandedExpression();
+ }
+ }
+
+ void printMessage() {
+ if ( itMessage != messages.end() ) {
+ stream << " '" << itMessage->message << '\'';
+ ++itMessage;
+ }
+ }
+
+ void printRemainingMessages( Colour::Code colour = dimColour() ) {
+ if ( itMessage == messages.end() )
+ return;
+
+ // using messages.end() directly yields compilation error:
+ std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+ const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+ {
+ Colour colourGuard( colour );
+ stream << " with " << pluralise( N, "message" ) << ':';
+ }
+
+ for(; itMessage != itEnd; ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+ stream << " '" << itMessage->message << '\'';
+ if ( ++itMessage != itEnd ) {
+ Colour colourGuard( dimColour() );
+ stream << " and";
+ }
+ }
+ }
+ }
+
+ private:
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ std::vector<MessageInfo> messages;
+ std::vector<MessageInfo>::const_iterator itMessage;
+ bool printInfoMessages;
+ };
+
+ // Colour, message variants:
+ // - white: No tests ran.
+ // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
+ // - white: Passed [both/all] N test cases (no assertions).
+ // - red: Failed N tests cases, failed M assertions.
+ // - green: Passed [both/all] N tests cases with M assertions.
+
+ std::string bothOrAll( std::size_t count ) const {
+ return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
+ }
+
+ void printTotals( const Totals& totals ) const {
+ if( totals.testCases.total() == 0 ) {
+ stream << "No tests ran.";
+ }
+ else if( totals.testCases.failed == totals.testCases.total() ) {
+ Colour colour( Colour::ResultError );
+ const std::string qualify_assertions_failed =
+ totals.assertions.failed == totals.assertions.total() ?
+ bothOrAll( totals.assertions.failed ) : std::string();
+ stream <<
+ "Failed " << bothOrAll( totals.testCases.failed )
+ << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << qualify_assertions_failed <<
+ pluralise( totals.assertions.failed, "assertion" ) << '.';
+ }
+ else if( totals.assertions.total() == 0 ) {
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.total() )
+ << pluralise( totals.testCases.total(), "test case" )
+ << " (no assertions).";
+ }
+ else if( totals.assertions.failed ) {
+ Colour colour( Colour::ResultError );
+ stream <<
+ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
+ }
+ else {
+ Colour colour( Colour::ResultSuccess );
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.passed )
+ << pluralise( totals.testCases.passed, "test case" ) <<
+ " with " << pluralise( totals.assertions.passed, "assertion" ) << '.';
+ }
+ }
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+ // These are all here to avoid warnings about not having any out of line
+ // virtual methods
+ NonCopyable::~NonCopyable() {}
+ IShared::~IShared() {}
+ IStream::~IStream() CATCH_NOEXCEPT {}
+ FileStream::~FileStream() CATCH_NOEXCEPT {}
+ CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+ DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
+ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+ IContext::~IContext() {}
+ IResultCapture::~IResultCapture() {}
+ ITestCase::~ITestCase() {}
+ ITestCaseRegistry::~ITestCaseRegistry() {}
+ IRegistryHub::~IRegistryHub() {}
+ IMutableRegistryHub::~IMutableRegistryHub() {}
+ IExceptionTranslator::~IExceptionTranslator() {}
+ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+ IReporter::~IReporter() {}
+ IReporterFactory::~IReporterFactory() {}
+ IReporterRegistry::~IReporterRegistry() {}
+ IStreamingReporter::~IStreamingReporter() {}
+ AssertionStats::~AssertionStats() {}
+ SectionStats::~SectionStats() {}
+ TestCaseStats::~TestCaseStats() {}
+ TestGroupStats::~TestGroupStats() {}
+ TestRunStats::~TestRunStats() {}
+ CumulativeReporterBase::SectionNode::~SectionNode() {}
+ CumulativeReporterBase::~CumulativeReporterBase() {}
+
+ StreamingReporterBase::~StreamingReporterBase() {}
+ ConsoleReporter::~ConsoleReporter() {}
+ CompactReporter::~CompactReporter() {}
+ IRunner::~IRunner() {}
+ IMutableContext::~IMutableContext() {}
+ IConfig::~IConfig() {}
+ XmlReporter::~XmlReporter() {}
+ JunitReporter::~JunitReporter() {}
+ TestRegistry::~TestRegistry() {}
+ FreeFunctionTestCase::~FreeFunctionTestCase() {}
+ IGeneratorInfo::~IGeneratorInfo() {}
+ IGeneratorsForTest::~IGeneratorsForTest() {}
+ WildcardPattern::~WildcardPattern() {}
+ TestSpec::Pattern::~Pattern() {}
+ TestSpec::NamePattern::~NamePattern() {}
+ TestSpec::TagPattern::~TagPattern() {}
+ TestSpec::ExcludedPattern::~ExcludedPattern() {}
+ Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {}
+
+ void Config::dummy() {}
+
+ namespace TestCaseTracking {
+ ITracker::~ITracker() {}
+ TrackerBase::~TrackerBase() {}
+ SectionTracker::~SectionTracker() {}
+ IndexTracker::~IndexTracker() {}
+ }
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+// Standard C/C++ Win32 Unicode wmain entry point
+extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+#endif
+
+ int result = Catch::Session().run( argc, argv );
+ return ( result < 0xff ? result : 0xff );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ Catch::registerTestMethods();
+ int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+ [pool drain];
+#endif
+
+ return ( result < 0xff ? result : 0xff );
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+# undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#else
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#else
+ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
+ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+ #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" )
+#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+
+#else
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#else
+#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
+ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+ #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" )
+#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" )
+
+using Catch::Detail::Approx;
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc
new file mode 100644
index 000000000..0b8c8378e
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests.cc
@@ -0,0 +1,339 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you 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.
+
+#include "../catch/catch.hpp"
+#include <thrift/parse/t_program.h>
+#include <thrift/generate/t_netcore_generator.h>
+#include "t_netcore_generator_functional_tests_helpers.h"
+
+TEST_CASE( "t_netcore_generator should generate valid enum", "[functional]" )
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+
+ std::pair<string, t_enum*> pair = TestDataGenerator::get_test_enum_data(program);
+ string expected_result = pair.first;
+ t_enum* test_enum = pair.second;
+
+ string file_path = test_enum->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_enum(out, test_enum));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete test_enum;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid void", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_void_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_THROWS(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid string with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_string_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid bool with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_bool_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid sbyte (i8) with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_i8_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid short (i16) with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_i16_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid integer (i32) with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_i32_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid long (i64) with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_i64_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should generate valid double with escaping keyword", "[functional]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+
+ std::pair<string, t_const*> pair = TestDataGenerator::get_test_double_const_data(gen);
+ string expected_result = pair.first;
+ t_const* const_ = pair.second;
+ vector<t_const*> consts_;
+ consts_.push_back(const_);
+
+ string file_path = const_->get_name() + ".cs";
+ ofstream out;
+ out.open(file_path.c_str());
+
+ REQUIRE_NOTHROW(gen->generate_consts(out, consts_));
+
+ out.close();
+
+ std::ifstream ifs(file_path);
+ string actual_result((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
+ std::remove(file_path.c_str());
+
+ REQUIRE(expected_result == actual_result);
+
+ delete const_;
+ delete gen;
+ delete program;
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc
new file mode 100644
index 000000000..92c170bb9
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.cc
@@ -0,0 +1,237 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you 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.
+
+#include <thrift/parse/t_program.h>
+#include "thrift/common.h"
+#include <thrift/generate/t_netcore_generator.h>
+#include "t_netcore_generator_functional_tests_helpers.h"
+
+const string TestDataGenerator::DEFAULT_FILE_HEADER = "/**" "\n"
+ " * Autogenerated by Thrift Compiler ()" "\n"
+ " *" "\n"
+ " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING" "\n"
+ " * @generated" "\n"
+ " */";
+
+std::pair<string, t_enum*> TestDataGenerator::get_test_enum_data(t_program* program)
+{
+ string expected_result = DEFAULT_FILE_HEADER +
+ "\n"
+ "\n"
+ "/// <summary>\n"
+ "/// TestDoc\n"
+ "/// </summary>\n"
+ "public enum TestName\n"
+ "{\n"
+ " None = 0,\n"
+ " First = 1,\n"
+ " Second = 2,\n"
+ "}\n";
+
+ t_enum* enum_ = new t_enum(program);
+ enum_->set_name("TestName");
+ enum_->set_doc("TestDoc");
+ enum_->append(new t_enum_value("None", 0));
+ enum_->append(new t_enum_value("First", 1));
+ enum_->append(new t_enum_value("Second", 2));
+
+ return std::pair<string, t_enum*>(expected_result, enum_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_void_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER;
+
+ t_type* type_ = new t_base_type("void", t_base_type::TYPE_VOID);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_string("VoidValue");
+
+ t_const* const_ = new t_const(type_, "void", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_string_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const string @string = \"StringValue\";\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("string", t_base_type::TYPE_STRING);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_string("StringValue");
+
+ t_const* const_ = new t_const(type_, "string", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_bool_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const bool @bool = true;\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("bool", t_base_type::TYPE_BOOL);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_integer(1);
+
+ t_const* const_ = new t_const(type_, "bool", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_i8_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const sbyte @sbyte = 127;\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("I8", t_base_type::TYPE_I8);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_integer(127);
+
+ t_const* const_ = new t_const(type_, "sbyte", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_i16_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const short @short = 32767;\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("i16", t_base_type::TYPE_I16);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_integer(32767);
+
+ t_const* const_ = new t_const(type_, "short", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_i32_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const int @int = 2147483647;\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("i32", t_base_type::TYPE_I32);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_integer(2147483647);
+
+ t_const* const_ = new t_const(type_, "int", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_i64_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const long @long = 9223372036854775807;\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("i64", t_base_type::TYPE_I64);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_integer(9223372036854775807);
+
+ t_const* const_ = new t_const(type_, "long", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
+
+std::pair<string, t_const*> TestDataGenerator::get_test_double_const_data(t_netcore_generator* gen)
+{
+ string expected_result = DEFAULT_FILE_HEADER + "\n" +gen->netcore_type_usings() +
+ "\n"
+ "public static class netcoreConstants\n"
+ "{\n"
+ " /// <summary>\n"
+ " /// TestDoc\n"
+ " /// </summary>\n"
+ " public const double @double = 9.22337e+18;\n"
+ "}\n";
+
+ t_type* type_ = new t_base_type("double", t_base_type::TYPE_DOUBLE);
+ type_->set_doc("TestDoc");
+
+ t_const_value* const_value_ = new t_const_value();
+ const_value_->set_double(9223372036854775807.1);
+
+ t_const* const_ = new t_const(type_, "double", const_value_);
+ const_->set_doc("TestDoc");
+
+ return std::pair<string, t_const*>(expected_result, const_);
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h
new file mode 100644
index 000000000..c6eaac22c
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_functional_tests_helpers.h
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you 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.
+
+#include <thrift/parse/t_program.h>
+
+class TestDataGenerator
+{
+public:
+ static const string DEFAULT_FILE_HEADER;
+
+ static std::pair<string, t_enum*> get_test_enum_data(t_program* program);
+ static std::pair<string, t_const*> get_test_void_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_string_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_bool_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_i8_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_i16_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_i32_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_i64_const_data(t_netcore_generator* gen);
+ static std::pair<string, t_const*> get_test_double_const_data(t_netcore_generator* gen);
+};
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc
new file mode 100644
index 000000000..0bcbeed19
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_helpers_tests.cc
@@ -0,0 +1,209 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you 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.
+
+#include "../catch/catch.hpp"
+#include <thrift/parse/t_program.h>
+#include <thrift/generate/t_netcore_generator.h>
+
+using std::vector;
+
+TEST_CASE("t_netcore_generator::netcore_type_usings() without option wcf should return valid namespaces", "[helpers]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "union", "union" } };
+ string option_string = "";
+
+ string expected_namespaces = "using System;\n"
+ "using System.Collections;\n"
+ "using System.Collections.Generic;\n"
+ "using System.Text;\n"
+ "using System.IO;\n"
+ "using System.Threading;\n"
+ "using System.Threading.Tasks;\n"
+ "using Thrift;\n"
+ "using Thrift.Collections;\n" + endl;
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+
+ REQUIRE_FALSE(gen->is_wcf_enabled());
+ REQUIRE(gen->netcore_type_usings() == expected_namespaces);
+
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator::netcore_type_usings() with option wcf should return valid namespaces", "[helpers]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+
+ string expected_namespaces_wcf = "using System;\n"
+ "using System.Collections;\n"
+ "using System.Collections.Generic;\n"
+ "using System.Text;\n"
+ "using System.IO;\n"
+ "using System.Threading;\n"
+ "using System.Threading.Tasks;\n"
+ "using Thrift;\n"
+ "using Thrift.Collections;\n"
+ "using System.ServiceModel;\n"
+ "using System.Runtime.Serialization;\n" + endl;
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+
+ REQUIRE(gen->is_wcf_enabled());
+ REQUIRE(gen->netcore_type_usings() == expected_namespaces_wcf);
+
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should contains latest C# keywords to normalize with @", "[helpers]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" } };
+ string option_string = "";
+ vector<string> current_keywords = {
+ { "abstract" },
+ { "as" },
+ { "base" },
+ { "bool" },
+ { "break" },
+ { "byte" },
+ { "case" },
+ { "catch" },
+ { "char" },
+ { "checked" },
+ { "class" },
+ { "const" },
+ { "continue" },
+ { "decimal" },
+ { "default" },
+ { "delegate" },
+ { "do" },
+ { "double" },
+ { "else" },
+ { "enum" },
+ { "event" },
+ { "explicit" },
+ { "extern" },
+ { "false" },
+ { "finally" },
+ { "fixed" },
+ { "float" },
+ { "for" },
+ { "foreach" },
+ { "goto" },
+ { "if" },
+ { "implicit" },
+ { "in" },
+ { "int" },
+ { "interface" },
+ { "internal" },
+ { "is" },
+ { "lock" },
+ { "long" },
+ { "namespace" },
+ { "new" },
+ { "null" },
+ { "object" },
+ { "operator" },
+ { "out" },
+ { "override" },
+ { "params" },
+ { "private" },
+ { "protected" },
+ { "public" },
+ { "readonly" },
+ { "ref" },
+ { "return" },
+ { "sbyte" },
+ { "sealed" },
+ { "short" },
+ { "sizeof" },
+ { "stackalloc" },
+ { "static" },
+ { "string" },
+ { "struct" },
+ { "switch" },
+ { "this" },
+ { "throw" },
+ { "true" },
+ { "try" },
+ { "typeof" },
+ { "uint" },
+ { "ulong" },
+ { "unchecked" },
+ { "unsafe" },
+ { "ushort" },
+ { "using" },
+ { "void" },
+ { "volatile" },
+ { "while" },
+ // Contextual Keywords
+ { "add" },
+ { "alias" },
+ { "ascending" },
+ { "async" },
+ { "await" },
+ { "descending" },
+ { "dynamic" },
+ { "from" },
+ { "get" },
+ { "global" },
+ { "group" },
+ { "into" },
+ { "join" },
+ { "let" },
+ { "orderby" },
+ { "partial" },
+ { "remove" },
+ { "select" },
+ { "set" },
+ { "value" },
+ { "var" },
+ { "when" },
+ { "where" },
+ { "yield" }
+ };
+
+ string missed_keywords = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+ gen->init_generator();
+ map<string, int> generators_keywords = gen->get_keywords_list();
+
+ for (vector<string>::iterator it = current_keywords.begin(); it != current_keywords.end(); ++it)
+ {
+ if (generators_keywords.find(*it) == generators_keywords.end())
+ {
+ missed_keywords = missed_keywords + *it + ",";
+ }
+ }
+
+ REQUIRE(missed_keywords == "");
+
+ delete gen;
+ delete program;
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc
new file mode 100644
index 000000000..ec17733bd
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/netcore/t_netcore_generator_initialization_tests.cc
@@ -0,0 +1,74 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you 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.
+
+#include "../catch/catch.hpp"
+#include <thrift/parse/t_program.h>
+#include <thrift/generate/t_netcore_generator.h>
+
+TEST_CASE( "t_netcore_generator should throw error with unknown options", "[initialization]" )
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "keys", "keys" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = nullptr;
+
+ REQUIRE_THROWS(gen = new t_netcore_generator(program, parsed_options, option_string));
+
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should create valid instance with valid options", "[initialization]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" }, { "nullable", "nullable"} };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = nullptr;
+
+ REQUIRE_NOTHROW(gen = new t_netcore_generator(program, parsed_options, option_string));
+ REQUIRE(gen != nullptr);
+ REQUIRE(gen->is_wcf_enabled());
+ REQUIRE(gen->is_nullable_enabled());
+ REQUIRE_FALSE(gen->is_hashcode_enabled());
+ REQUIRE_FALSE(gen->is_serialize_enabled());
+ REQUIRE_FALSE(gen->is_union_enabled());
+
+ delete gen;
+ delete program;
+}
+
+TEST_CASE("t_netcore_generator should pass init succesfully", "[initialization]")
+{
+ string path = "CassandraTest.thrift";
+ string name = "netcore";
+ map<string, string> parsed_options = { { "wcf", "wcf" },{ "nullable", "nullable" } };
+ string option_string = "";
+
+ t_program* program = new t_program(path, name);
+ t_netcore_generator* gen = new t_netcore_generator(program, parsed_options, option_string);
+
+ REQUIRE_NOTHROW(gen->init_generator());
+
+ delete gen;
+ delete program;
+}
diff --git a/src/jaegertracing/thrift/compiler/cpp/tests/tests_main.cc b/src/jaegertracing/thrift/compiler/cpp/tests/tests_main.cc
new file mode 100644
index 000000000..21d09b9f0
--- /dev/null
+++ b/src/jaegertracing/thrift/compiler/cpp/tests/tests_main.cc
@@ -0,0 +1,19 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you 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.
+
+#define CATCH_CONFIG_MAIN
+#include "catch/catch.hpp"