thrift-compiler_0.9.1/0000755000175000017500000000000012330037326015476 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/configure.ac0000600000175000017500000000153512330037326017760 0ustar eevanseevans00000000000000 AC_PREREQ([2.65]) AC_INIT([thrift], [0.9.1]) #AM_CONFIG_SRCDIR([cpp/thrifty.h]) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile cpp/Makefile cpp/version.h ]) # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_CPP AC_PROG_YACC AC_PROG_LEX AC_PROG_RANLIB AM_PROG_CC_C_O # Checks for libraries. # Checks for header files. AC_FUNC_ALLOCA AC_CHECK_HEADERS([inttypes.h libintl.h limits.h malloc.h stddef.h stdint.h stdlib.h string.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_CHECK_HEADER_STDBOOL AC_C_INLINE AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_INT8_T AC_TYPE_SIZE_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_ERROR_AT_LINE AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([memset mkdir realpath strdup strerror]) AC_OUTPUT thrift-compiler_0.9.1/tutorial/0000700000175000017500000000000012330037326017327 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/tutorial/tutorial.thrift0000644000175000017500000001135612330037326022434 0ustar eevanseevans00000000000000/* * 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 Tutorial # Mark Slee (mcslee@facebook.com) # # This file aims to teach you how to use Thrift, in a .thrift file. Neato. The # first thing to notice is that .thrift files support standard shell comments. # This lets you make your thrift file executable and include your Thrift build # step on the top line. And you can place comments like this anywhere you like. # # Before running this file, you will need to have installed the thrift compiler # into /usr/local/bin. /** * The first thing to know about are types. The available types in Thrift are: * * bool Boolean, one byte * byte Signed byte * i16 Signed 16-bit integer * i32 Signed 32-bit integer * i64 Signed 64-bit integer * double 64-bit floating point value * string String * binary Blob (byte array) * map Map from one type to another * list Ordered list of one type * set Set of unique elements of one type * * Did you also notice that Thrift supports C style comments? */ // Just in case you were wondering... yes. We support simple C comments too. /** * Thrift files can reference other Thrift files to include common struct * and service definitions. These are found using the current path, or by * searching relative to any paths specified with the -I compiler flag. * * Included objects are accessed using the name of the .thrift file as a * prefix. i.e. shared.SharedObject */ include "shared.thrift" /** * Thrift files can namespace, package, or prefix their output in various * target languages. */ namespace cpp tutorial namespace d tutorial namespace java tutorial namespace php tutorial namespace perl tutorial /** * Thrift lets you do typedefs to get pretty names for your types. Standard * C style here. */ typedef i32 MyInteger /** * Thrift also lets you define constants for use across languages. Complex * types and structs are specified using JSON notation. */ const i32 INT32CONSTANT = 9853 const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} /** * You can define enums, which are just 32 bit integers. Values are optional * and start at 1 if not supplied, C style again. */ enum Operation { ADD = 1, SUBTRACT = 2, MULTIPLY = 3, DIVIDE = 4 } /** * Structs are the basic complex data structures. They are comprised of fields * which each have an integer identifier, a type, a symbolic name, and an * optional default value. * * Fields can be declared "optional", which ensures they will not be included * in the serialized output if they aren't set. Note that this requires some * manual management in some languages. */ struct Work { 1: i32 num1 = 0, 2: i32 num2, 3: Operation op, 4: optional string comment, } /** * Structs can also be exceptions, if they are nasty. */ exception InvalidOperation { 1: i32 what, 2: string why } /** * Ahh, now onto the cool part, defining a service. Services just need a name * and can optionally inherit from another service using the extends keyword. */ service Calculator extends shared.SharedService { /** * A method definition looks like C code. It has a return type, arguments, * and optionally a list of exceptions that it may throw. Note that argument * lists and exception lists are specified using the exact same syntax as * field lists in struct or exception definitions. */ void ping(), i32 add(1:i32 num1, 2:i32 num2), i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), /** * This method has a oneway modifier. That means the client only makes * a request and does not listen for any response at all. Oneway methods * must be void. */ oneway void zip() } /** * That just about covers the basics. Take a look in the test/ folder for more * detailed examples. After you run this file, your generated code shows up * in folders with names gen-. The generated code isn't too scary * to look at. It even has pretty indentation. */ thrift-compiler_0.9.1/tutorial/shared.thrift0000644000175000017500000000225112330037326022031 0ustar eevanseevans00000000000000/* * 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 Thrift file can be included by other Thrift files that want to share * these definitions. */ namespace cpp shared namespace d share // "shared" would collide with the eponymous D keyword. namespace java shared namespace perl shared namespace php shared struct SharedStruct { 1: i32 key 2: string value } service SharedService { SharedStruct getStruct(1: i32 key) } thrift-compiler_0.9.1/cpp/0000755000175000017500000000000012204170451016255 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/cpp/compiler.vcxproj0000644000175000017500000002722512203157755021527 0ustar eevanseevans00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {89975A1A-F799-4556-98B8-64E30AB39A90} Win32Proj compiler Application true MultiByte Application true MultiByte Application false true MultiByte Application false true MultiByte true $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) thrift $(ExecutablePath);C:\Program Files (x86)\Git\bin true $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) thrift $(ExecutablePath);C:\Program Files (x86)\Git\bin false $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) thrift $(ExecutablePath);C:\Program Files (x86)\Git\bin false $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) thrift $(ExecutablePath);C:\Program Files (x86)\Git\bin Level3 Disabled WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) config.h CompileAsCpp Console true flex -o "src\\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" --defines="src/thrifty.hh" src/thrifty.yy Level3 Disabled WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) config.h CompileAsCpp Console true flex -o "src\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" --defines="src/thrifty.hh" src/thrifty.yy Level3 MaxSpeed true true WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) config.h CompileAsCpp Console true true true flex -o "src\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" --defines="src/thrifty.hh" src/thrifty.yy Level3 MaxSpeed true true WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) config.h CompileAsCpp Console true true true flex -o "src\thriftl.cc" src/thriftl.ll bison -y -o "src\thrifty.cc" --defines="src/thrifty.hh" src/thrifty.yy thrift-compiler_0.9.1/cpp/README0000644000175000017500000000000012203157755017136 0ustar eevanseevans00000000000000thrift-compiler_0.9.1/cpp/src/0000755000175000017500000000000012204170450017043 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/cpp/src/thrifty.yy0000644000175000017500000007062212203157755021142 0ustar eevanseevans00000000000000%{ /* * 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. * */ #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS #include #ifndef _MSC_VER #include #else #include #endif #include #include "main.h" #include "globals.h" #include "parse/t_program.h" #include "parse/t_scope.h" /** * 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; 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 tok_identifier %token tok_literal %token tok_doctext %token tok_st_identifier /** * Constant values */ %token tok_int_constant %token tok_dub_constant /** * Header keywords */ %token tok_include %token tok_namespace %token tok_cpp_namespace %token tok_cpp_include %token tok_cpp_type %token tok_php_namespace %token tok_py_module %token tok_perl_package %token tok_java_package %token tok_xsd_all %token tok_xsd_optional %token tok_xsd_nillable %token tok_xsd_namespace %token tok_xsd_attrs %token tok_ruby_namespace %token tok_smalltalk_category %token tok_smalltalk_prefix %token tok_cocoa_prefix %token tok_csharp_namespace %token tok_delphi_namespace /** * Base datatype keywords */ %token tok_void %token tok_bool %token tok_byte %token tok_string %token tok_binary %token tok_slist %token tok_senum %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 /** * Grammar nodes */ %type BaseType %type SimpleBaseType %type ContainerType %type SimpleContainerType %type MapType %type SetType %type ListType %type Definition %type TypeDefinition %type Typedef %type TypeAnnotations %type TypeAnnotationList %type TypeAnnotation %type Field %type FieldIdentifier %type FieldRequiredness %type FieldType %type FieldValue %type FieldList %type Enum %type EnumDefList %type EnumDef %type Senum %type SenumDefList %type SenumDef %type Const %type ConstValue %type ConstList %type ConstListContents %type ConstMap %type ConstMapContents %type StructHead %type Struct %type Xception %type Service %type Function %type FunctionType %type FunctionList %type Throws %type Extends %type Oneway %type XsdAll %type XsdOptional %type XsdNillable %type XsdAttributes %type CppType %type 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"); /* TODO(dreiss): Decide whether full-program doctext is worth the trouble. if ($1 != NULL) { g_program->set_doc($1); } */ 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 { pdebug("Header -> tok_namespace tok_identifier tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace($2, $3); } } | tok_namespace '*' tok_identifier { pdebug("Header -> tok_namespace * tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("*", $3); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_cpp_namespace tok_identifier { pwarning(1, "'cpp_namespace' is deprecated. Use 'namespace cpp' instead"); pdebug("Header -> tok_cpp_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("cpp", $2); } } | tok_cpp_include tok_literal { pdebug("Header -> tok_cpp_include tok_literal"); if (g_parse_mode == PROGRAM) { g_program->add_cpp_include($2); } } | tok_php_namespace tok_identifier { pwarning(1, "'php_namespace' is deprecated. Use 'namespace php' instead"); pdebug("Header -> tok_php_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("php", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_py_module tok_identifier { pwarning(1, "'py_module' is deprecated. Use 'namespace py' instead"); pdebug("Header -> tok_py_module tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("py", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_perl_package tok_identifier { pwarning(1, "'perl_package' is deprecated. Use 'namespace perl' instead"); pdebug("Header -> tok_perl_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("perl", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_ruby_namespace tok_identifier { pwarning(1, "'ruby_namespace' is deprecated. Use 'namespace rb' instead"); pdebug("Header -> tok_ruby_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("rb", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_smalltalk_category tok_st_identifier { pwarning(1, "'smalltalk_category' is deprecated. Use 'namespace smalltalk.category' instead"); pdebug("Header -> tok_smalltalk_category tok_st_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("smalltalk.category", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_smalltalk_prefix tok_identifier { pwarning(1, "'smalltalk_prefix' is deprecated. Use 'namespace smalltalk.prefix' instead"); pdebug("Header -> tok_smalltalk_prefix tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("smalltalk.prefix", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_java_package tok_identifier { pwarning(1, "'java_package' is deprecated. Use 'namespace java' instead"); pdebug("Header -> tok_java_package tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("java", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_cocoa_prefix tok_identifier { pwarning(1, "'cocoa_prefix' is deprecated. Use 'namespace cocoa' instead"); pdebug("Header -> tok_cocoa_prefix tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("cocoa", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_xsd_namespace tok_literal { pwarning(1, "'xsd_namespace' is deprecated. Use 'namespace xsd' instead"); pdebug("Header -> tok_xsd_namespace tok_literal"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("cocoa", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_csharp_namespace tok_identifier { pwarning(1, "'csharp_namespace' is deprecated. Use 'namespace csharp' instead"); pdebug("Header -> tok_csharp_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("csharp", $2); } } /* TODO(dreiss): Get rid of this once everyone is using the new hotness. */ | tok_delphi_namespace tok_identifier { pwarning(1, "'delphi_namespace' is deprecated. Use 'namespace delphi' instead"); pdebug("Header -> tok_delphi_namespace tok_identifier"); if (g_parse_mode == PROGRAM) { g_program->set_namespace("delphi", $2); } } Include: tok_include tok_literal { pdebug("Include -> tok_include tok_literal"); 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); } } Typedef: tok_typedef FieldType tok_identifier TypeAnnotations { pdebug("TypeDef -> tok_typedef FieldType tok_identifier"); t_typedef *td = new t_typedef(g_program, $2, $3); $$ = td; if ($4 != NULL) { $$->annotations_ = $4->annotations_; delete $4; } } CommaOrSemicolonOptional: ',' {} | ';' {} | {} Enum: tok_enum tok_identifier '{' EnumDefList '}' TypeAnnotations { pdebug("Enum -> tok_enum tok_identifier { EnumDefList }"); $$ = $4; $$->set_name($2); if ($6 != NULL) { $$->annotations_ = $6->annotations_; delete $6; } $$->resolve_values(); // make constants for all the enum values if (g_parse_mode == PROGRAM) { const std::vector& enum_values = $$->get_constants(); std::vector::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); } EnumDef: CaptureDocText tok_identifier '=' tok_int_constant TypeAnnotations CommaOrSemicolonOptional { pdebug("EnumDef -> tok_identifier = tok_int_constant"); if ($4 < 0) { pwarning(1, "Negative value supplied for enum %s.\n", $2); } if ($4 > INT_MAX) { pwarning(1, "64-bit value supplied for enum %s.\n", $2); } $$ = new t_enum_value($2, $4); if ($1 != NULL) { $$->set_doc($1); } if ($5 != NULL) { $$->annotations_ = $5->annotations_; delete $5; } } | CaptureDocText tok_identifier TypeAnnotations CommaOrSemicolonOptional { pdebug("EnumDef -> tok_identifier"); $$ = new t_enum_value($2); if ($1 != NULL) { $$->set_doc($1); } if ($3 != NULL) { $$->annotations_ = $3->annotations_; delete $3; } } Senum: tok_senum tok_identifier '{' SenumDefList '}' TypeAnnotations { pdebug("Senum -> tok_senum tok_identifier { SenumDefList }"); $$ = 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) { 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 }"); $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 }"); $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 }"); $$ = $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 { $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 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", $5); if (g_strict >= 192) { yyerror("Implicit field keys are deprecated and not allowed with -strict"); exit(1); } } $$ = new t_field($4, $5, $2.value); $$->set_req($3); if ($6 != NULL) { g_scope->resolve_const_value($6, $4); validate_field_value($$, $6); $$->set_value($6); } $$->set_xsd_optional($7); $$->set_xsd_nillable($8); if ($1 != NULL) { $$->set_doc($1); } if ($9 != NULL) { $$->set_xsd_attrs($9); } if ($10 != NULL) { $$->annotations_ = $10->annotations_; delete $10; } } 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 = $1 - 1; $$.value = $1; $$.auto_assigned = false; } else { pwarning(1, "Nonpositive value (%"PRIi64") not allowed as a field key.\n", $1); $$.value = y_field_val--; $$.auto_assigned = true; } } else { $$.value = $1; $$.auto_assigned = false; } } | { $$.value = y_field_val--; $$.auto_assigned = true; } 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) { yyerror("Type \"%s\" has not been defined.", $1); exit(1); } } } | 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($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_byte { pdebug("BaseType -> tok_byte"); $$ = g_type_byte; } | 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 "); $$ = 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"); $$ = new t_set($4); if ($2 != NULL) { ((t_container*)$$)->set_cpp_name(std::string($2)); } } ListType: tok_list '<' FieldType '>' CppType { pdebug("ListType -> tok_list"); $$ = 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 '=' tok_literal CommaOrSemicolonOptional { pdebug("TypeAnnotation -> tok_identifier = tok_literal"); $$ = new t_annotation; $$->key = $1; $$->val = $3; } %% thrift-compiler_0.9.1/cpp/src/md5.h0000644000175000017500000000650012203157755017716 0ustar eevanseevans00000000000000/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ thrift-compiler_0.9.1/cpp/src/globals.h0000644000175000017500000000711312203157755020655 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include /** * 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; /** * 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_byte; extern t_type* g_type_i16; extern t_type* g_type_i32; extern t_type* g_type_i64; extern t_type* g_type_double; /** * 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; /** * 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 thrift-compiler_0.9.1/cpp/src/generate/0000755000175000017500000000000012204170451020636 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/cpp/src/generate/t_generator.cc0000644000175000017500000001333612203157755023477 0ustar eevanseevans00000000000000/* * 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 "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 enums = program_->get_enums(); vector::iterator en_iter; for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { generate_enum(*en_iter); } // Generate typedefs vector typedefs = program_->get_typedefs(); vector::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 objects = program_->get_objects(); vector::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); } } // Generate constants vector consts = program_->get_consts(); generate_consts(consts); // Generate services vector services = program_->get_services(); vector::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(); } string t_generator::escape_string(const string &in) const { string result = ""; for (string::const_iterator it = in.begin(); it < in.end(); it++) { std::map::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 consts) { vector::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { generate_const(*c_iter); } } void t_generator::generate_docstring_comment(ofstream& out, const string& comment_start, const string& line_prefix, const string& contents, const string& comment_end) { if (comment_start != "") indent(out) << comment_start; stringstream docs(contents, ios_base::in); while (!docs.eof()) { 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 << std::endl; } else if (strlen(line) > 0 || !docs.eof()) { // skip the empty last line indent(out) << line_prefix << line << std::endl; } } if (comment_end != "") 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; } t_generator* t_generator_registry::get_generator(t_program* program, const string& options) { string::size_type colon = options.find(':'); string language = options.substr(0, colon); map parsed_options; 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; } } 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_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); } thrift-compiler_0.9.1/cpp/src/generate/t_gv_generator.cc0000644000175000017500000002370012203157755024167 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "t_generator.h" #include "platform.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& parsed_options, const std::string& option_string) : t_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-gv"; std::map::const_iterator iter; iter = parsed_options.find("exceptions"); exception_arrows = (iter != parsed_options.end()); } /** * Init and end of generator */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_service (t_service* tservice); protected: /** * Helpers */ void print_type(t_type* ttype, string struct_field_ref); void print_const_value(t_const_value* tvalue); private: std::ofstream f_out_; std::list 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::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 values = tenum->get_constants(); vector::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_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 members = tstruct->get_members(); vector::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 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_ << (((t_base_type*)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_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 map_elems = tvalue->get_map(); map::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(map_iter->first); f_out_ << " = "; print_const_value(map_iter->second); } f_out_ << " \\}"; } break; case t_const_value::CV_LIST: { f_out_ << "\\{ "; vector list_elems = tvalue->get_list();; vector::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(*list_iter); } f_out_ << " \\}"; } 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 functions = tservice->get_functions(); vector::iterator fn_iter = functions.begin(); for ( ; fn_iter != functions.end(); fn_iter++) { string fn_name = (*fn_iter)->get_name(); f_out_ << "function_" << fn_name; f_out_ << "[label=\"function " << escape_string(fn_name); f_out_ << " :: "; print_type((*fn_iter)->get_returntype(), "function_" + fn_name + ":return_type"); vector args = (*fn_iter)->get_arglist()->get_members(); vector::iterator arg_iter = args.begin(); for ( ; arg_iter != args.end(); arg_iter++) { f_out_ << "|get_name() << ">"; f_out_ << (*arg_iter)->get_name(); if ((*arg_iter)->get_value() != NULL) { f_out_ << " = "; print_const_value((*arg_iter)->get_value()); } f_out_ << " :: "; print_type((*arg_iter)->get_type(), "function_" + fn_name + ":param_" + (*arg_iter)->get_name()); } // end of node f_out_ << "\"];" << endl; // Exception edges if (exception_arrows) { vector excepts = (*fn_iter)->get_xceptions()->get_members(); vector::iterator ex_iter = excepts.begin(); for ( ; ex_iter != excepts.end(); ex_iter++) { edges.push_back("function_" + 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" ) thrift-compiler_0.9.1/cpp/src/generate/t_js_generator.cc0000644000175000017500000015754712203157755024210 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "platform.h" #include "version.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 #include "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& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("node"); gen_node_ = (iter != parsed_options.end()); iter = parsed_options.find("jquery"); gen_jquery_ = (iter != parsed_options.end()); if (gen_node_) { out_dir_base_ = "gen-nodejs"; } else { out_dir_base_ = "gen-js"; } } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_exported=true); void generate_js_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_js_struct_writer(std::ofstream& 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::ofstream &out, t_field* tfield, std::string prefix="", bool inclass=false); void generate_deserialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream &out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream &out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream &out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream &out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream &out, t_map* tmap, std::string kiter, std::string viter); void generate_serialize_set_element (std::ofstream &out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream &out, t_list* tlist, std::string iter); /** * Helper rendering functions */ std::string js_includes(); std::string render_includes(); 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 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"; } std::vector js_namespace_pieces(t_program* p) { std::string ns = p->get_namespace("js"); std::string::size_type loc; std::vector 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 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); } std::string js_namespace(t_program* p) { std::string ns = p->get_namespace("js"); if (ns.size() > 0) { ns += "."; } return ns; } private: /** * True iff 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_; /** * File streams */ std::ofstream f_types_; std::ofstream f_service_; }; /** * 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()); string outdir = get_out_dir(); // Make output file string f_types_name = outdir+program_->get_name()+"_types.js"; f_types_.open(f_types_name.c_str()); // Print header f_types_ << autogen_comment() << js_includes() << endl << render_includes() << endl; if (gen_node_) { f_types_ << "var ttypes = module.exports = {};" << endl; } string pns; //setup the namespace // TODO should the namespace just be in the directory structure for node? vector 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; } } } /** * Prints standard js imports */ string t_js_generator::js_includes() { if (gen_node_) { return string("var Thrift = require('thrift').Thrift;"); } string inc; return inc; } /** * Renders all the imports necessary for including another Thrift program */ string t_js_generator::render_includes() { if (gen_node_) { const vector& includes = program_->get_includes(); string result = ""; for (size_t i = 0; i < includes.size(); ++i) { result += "var " + includes[i]->get_name() + "_ttypes = require('./" + includes[i]->get_name() + "_types')\n"; } if (includes.size() > 0) { result += "\n"; } return result; } string inc; return inc; } /** * Close up (or down) some filez. */ void t_js_generator::close_generator() { // Close types file f_types_.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())<get_name()<<" = {"< constants = tenum->get_constants(); vector::iterator c_iter; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); f_types_ << "'" << (*c_iter)->get_name() << "' : " << value; if (c_iter != constants.end()-1) { f_types_ << ","; } f_types_ << endl; } f_types_ << "};"<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; } /** * 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 << "'" << value->get_string() << "'"; break; case t_base_type::TYPE_BOOL: out << (value->get_integer() > 0 ? "true" : "false"); break; case t_base_type::TYPE_BYTE: 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 << "new " << js_type_namespace(type->get_program()) << type->get_name() << "({" << endl; indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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(); } if (v_iter != val.begin()) out << ","; out << render_const_value(g_type_string, v_iter->first); out << " : "; out << render_const_value(field_type, v_iter->second); } 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 << "{"; const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { if (v_iter != val.begin()) out << "," << endl; out << render_const_value(ktype, v_iter->first); out << " : "; out << render_const_value(vtype, v_iter->second); } out << 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(); } out << "["; const vector& val = value->get_list(); vector::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); } /** * 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(ofstream& out, t_struct* tstruct, bool is_exception, bool is_exported) { const vector& members = tstruct->get_members(); vector::const_iterator m_iter; indent_up(); if (gen_node_) { if (is_exported) { out << js_namespace(tstruct->get_program()) << tstruct->get_name() << " = " << "module.exports." << tstruct->get_name() << " = function(args) {\n"; } else { out << js_namespace(tstruct->get_program()) << tstruct->get_name() << " = function(args) {\n"; } } else { out << js_namespace(tstruct->get_program()) << tstruct->get_name() <<" = function(args) {\n"; } if (gen_node_ && is_exception) { out << indent() << "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; } } // 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; } } out << indent() << "if (args) {" << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { out << indent() << indent() << "if (args." << (*m_iter)->get_name() << " !== undefined) {" << endl << indent() << indent() << indent() << "this." << (*m_iter)->get_name() << " = args." << (*m_iter)->get_name() << ";" << endl << indent() << indent() << "}" << endl; } out << indent() << "}" << endl; } indent_down(); out << "};\n"; if (is_exception) { out << "Thrift.inherits(" << js_namespace(tstruct->get_program()) << tstruct->get_name() << ", Thrift.TException);" << endl; out << js_namespace(tstruct->get_program())<get_name() <<".prototype.name = '" << tstruct->get_name() << "';" << endl; } else { //init prototype out << js_namespace(tstruct->get_program())<get_name() <<".prototype = {};\n"; } generate_js_struct_reader(out, tstruct); generate_js_struct_writer(out, tstruct); } /** * Generates the read() method for a struct */ void t_js_generator::generate_js_struct_reader(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; out << js_namespace(tstruct->get_program())<get_name() << ".prototype.read = function(input) {"<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(); out << indent() << "};" << endl << endl; } /** * Generates the write() method for a struct */ void t_js_generator::generate_js_struct_writer(ofstream& out, t_struct* tstruct) { string name = tstruct->get_name(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; out << js_namespace(tstruct->get_program())<< tstruct->get_name() << ".prototype.write = function(output) {"<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 <get_extends() != NULL) { f_service_ << "var " << tservice->get_extends()->get_name() << " = require('./" << tservice->get_extends()->get_name() << "')" << endl << "var " << tservice->get_extends()->get_name() << "Client = " << tservice->get_extends()->get_name() << ".Client" << endl << "var " << tservice->get_extends()->get_name() << "Processor = " << tservice->get_extends()->get_name() << ".Processor" << endl; } f_service_ << "var 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(); } /** * 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 functions = tservice->get_functions(); vector::iterator f_iter; f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Processor = " << "exports.Processor = function(handler) "; scope_up(f_service_); f_service_ << indent() << "this._handler = handler" << endl; scope_down(f_service_); if (tservice->get_extends() != NULL) { indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program()) << service_name_ << "Processor, " << tservice->get_extends()->get_name() << "Processor)" << endl; } // Generate the server implementation indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Processor.prototype.process = function(input, output) "; scope_up(f_service_); f_service_ << indent() << "var 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() << " var 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; 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); } } /** * 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) { indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ << "Processor.prototype.process_" + tfunction->get_name() + " = function(seqid, input, output) "; scope_up(f_service_); string argsname = js_namespace(program_)+ service_name_ + "_" + tfunction->get_name() + "_args"; string resultname = js_namespace(program_)+ service_name_ + "_" + tfunction->get_name() + "_result"; f_service_ << indent() << "var 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& fields = arg_struct->get_members(); vector::const_iterator f_iter; f_service_ << indent() << "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(); } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { f_service_ << ")" << endl; scope_down(f_service_); f_service_ << endl; return; } if (!first) { f_service_ << ", "; } f_service_ << "function (err, result) {" << endl; indent_up(); f_service_ << indent() << "var result = new " << resultname << "((err != null ? err : {success: result}));" << endl << indent() << "output.writeMessageBegin(\"" << tfunction->get_name() << "\", Thrift.MessageType.REPLY, seqid);" << endl << indent() << "result.write(output);" << endl << indent() << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl; indent_down(); indent(f_service_) << "})" << endl; scope_down(f_service_); 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) { vector functions = tservice->get_functions(); vector::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); } } /** * 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& fields = xs->get_members(); vector::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) { string extends = ""; if (gen_node_) { f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Client = " << "exports.Client = function(output, pClass) {"<get_program()) << service_name_ << "Client = function(input, output) {"<get_extends() != NULL) { indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program()) << service_name_ << "Client, " << tservice->get_extends()->get_name() << "Client)" << endl; } else { //init prototype indent(f_service_) << js_namespace(tservice->get_program())< functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::const_iterator fld_iter; string funname = (*f_iter)->get_name(); string arglist = argument_list(arg_struct); // Open function f_service_ << js_namespace(tservice->get_program())<is_oneway()) { f_service_ << indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "this.recv_" << funname << "();" << endl; } if (gen_jquery_) { indent_down(); f_service_ << indent() << "} else {" << endl; indent_up(); f_service_ << indent() << "var 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; } indent_down(); f_service_ << "};" << endl << endl; // Send function f_service_ << js_namespace(tservice->get_program())<get_name() + "_args"; // Serialize the request header f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name() << "', Thrift.MessageType.CALL, this.seqid);" << endl; f_service_ << indent() << "var args = 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; } // Write to the stream f_service_ << indent() << "args.write(" << outputVar << ");" << endl << indent() << outputVar << ".writeMessageEnd();" << endl; if (gen_node_) { f_service_ << indent() << "return this.output.flush();" << endl; } else { if (gen_jquery_) { f_service_ << indent() << "return this.output.getTransport().flush(callback);" << endl; } else { f_service_ << indent() << "return this.output.getTransport().flush();" << endl; } } indent_down(); f_service_ << "};" << endl; if (!(*f_iter)->is_oneway()) { std::string resultname = js_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_result"; if (gen_node_) { // Open function f_service_ << endl << js_namespace(tservice->get_program())<get_name() << " = function(input,mtype,rseqid) {" << endl; } else { t_struct noargs(program_); t_function recv_function((*f_iter)->get_returntype(), string("recv_") + (*f_iter)->get_name(), &noargs); // Open function f_service_ << endl << js_namespace(tservice->get_program())<get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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 function indent_down(); f_service_ << "};"<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 << " = 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()"; break; case t_base_type::TYPE_BOOL: out << "readBool()"; break; case t_base_type::TYPE_BYTE: 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 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(ofstream &out, t_struct* tstruct, string prefix) { out << indent() << prefix << " = new " << js_type_namespace(tstruct->get_program())<get_name() << "();" << endl << indent() << prefix << ".read(input);" << endl; } void t_js_generator::generate_deserialize_container(ofstream &out, t_type* ttype, string prefix) { string size = tmp("_size"); string ktype = tmp("_ktype"); string vtype = tmp("_vtype"); string etype = tmp("_etype"); string rtmp3 = tmp("_rtmp3"); t_field fsize(g_type_i32, size); t_field fktype(g_type_byte, ktype); t_field fvtype(g_type_byte, vtype); t_field fetype(g_type_byte, etype); out << indent() << "var " << size << " = 0;" << endl; out << indent() << "var " << rtmp3 << ";" << endl; // Declare variables, read header if (ttype->is_map()) { out << indent() << prefix << " = {};" << endl << indent() << "var " << ktype << " = 0;" << endl << indent() << "var " << vtype << " = 0;" << endl; out << indent() << rtmp3 << " = input.readMapBegin();" << endl; out << indent() << ktype << " = " << rtmp3 << ".ktype;" << endl; out << indent() << vtype << " = " << rtmp3 << ".vtype;" << endl; out << indent() << size << " = " << rtmp3 << ".size;" << endl; } else if (ttype->is_set()) { out << indent() << prefix << " = [];" << endl << indent() << "var " << etype << " = 0;" << endl << indent() << rtmp3 << " = input.readSetBegin();" << endl << indent() << etype << " = " << rtmp3 << ".etype;"<is_list()) { out << indent() << prefix << " = [];" << endl << indent() << "var " << etype << " = 0;" << endl << indent() << rtmp3 << " = input.readListBegin();" << endl << indent() << etype << " = " << rtmp3 << ".etype;"<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(ofstream &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(ofstream &out, t_set* tset, string prefix) { string elem = tmp("elem"); t_field felem(tset->get_elem_type(), elem); indent(out) << "var " << elem << " = null;" << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".push(" << elem << ");" << endl; } void t_js_generator::generate_deserialize_list_element(ofstream &out, t_list* tlist, string prefix) { string elem = tmp("elem"); t_field felem(tlist->get_elem_type(), elem); indent(out) << "var " << 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(ofstream &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) << "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_BYTE: 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 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(ofstream &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(ofstream &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 (var "<is_set()) { string iter = tmp("iter"); indent(out) << "for (var "<is_list()) { string iter = tmp("iter"); indent(out) << "for (var "<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(ofstream &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(ofstream &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(ofstream &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 = "var " + 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_BYTE: 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 "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& fields = tstruct->get_members(); vector::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 "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_BYTE: 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 "INVALID TYPE IN type_to_enum: " + type->get_name(); } THRIFT_REGISTER_GENERATOR(js, "Javascript", " jquery: Generate jQuery compatible code.\n" " node: Generate node.js compatible code.\n") thrift-compiler_0.9.1/cpp/src/generate/t_rb_generator.cc0000644000175000017500000011441212203157755024157 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "t_oop_generator.h" #include "platform.h" #include "version.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& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; out_dir_base_ = "gen-rb"; require_rubygems_ = (parsed_options.find("rubygems") != parsed_options.end()); } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_union (t_struct* tunion); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); t_rb_ofstream& render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value); /** * Struct generation code */ 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(t_type* ttype); std::string full_type_name(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::vector ruby_modules(t_program* p) { std::string ns = p->get_namespace("rb"); std::vector 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); void end_namespace(t_rb_ofstream&, std::vector); private: /** * File streams */ t_rb_ofstream f_types_; t_rb_ofstream f_consts_; t_rb_ofstream f_service_; /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */ bool require_rubygems_; }; /** * Prepares for file generation by opening up the necessary file output * streams. * * @param tprogram The program to generate */ void t_rb_generator::init_generator() { // Make output directory MKDIR(get_out_dir().c_str()); // Make output file string f_types_name = get_out_dir()+underscore(program_name_)+"_types.rb"; f_types_.open(f_types_name.c_str()); string f_consts_name = get_out_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 '" << 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& includes = program_->get_includes(); string result = ""; for (size_t i = 0; i < includes.size(); ++i) { result += "require '" + underscore(includes[i]->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 constants = tenum->get_constants(); vector::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 = {"; bool first = true; for(c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { // Populate the hash int value = (*c_iter)->get_value(); first ? first = false : 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(["; first = true; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { // Populate the set first ? first = false : 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_BYTE: 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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 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& fields = tstruct->get_members(); vector::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& members = tstruct->get_members(); if (members.size() == 1) { vector::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& fields = tstruct->get_members(); vector::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& fields = tstruct->get_members(); vector::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 modules) { for (vector::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { out.indent() << "module " << *m_iter << endl; out.indent_up(); } } void t_rb_generator::end_namespace(t_rb_ofstream& out, vector modules) { for (vector::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 = get_out_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) { f_service_ << "require '" << underscore(tservice->get_extends()->get_name()) << "'" << endl; } f_service_ << "require '" << 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 functions = tservice->get_functions(); vector::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& fields = xs->get_members(); vector::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 functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::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"); f_service_.indent() << "send_message('" << 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& xceptions = xs->get_members(); vector::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 functions = tservice->get_functions(); vector::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& xceptions = xs->get_members(); vector::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& fields = arg_struct->get_members(); vector::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& fields = tstruct->get_members(); vector::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(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(t_type* ttype) { string prefix = "::"; vector modules = ruby_modules(ttype->get_program()); for (vector::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { prefix += *m_iter + "::"; } 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_BYTE: 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(); } 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& fields = tstruct->get_members(); vector::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& fields = tstruct->get_members(); vector::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") thrift-compiler_0.9.1/cpp/src/generate/t_py_generator.cc0000644000175000017500000025336212203157755024214 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "t_generator.h" #include "platform.h" #include "version.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 /** * Python code generator. * */ class t_py_generator : public t_generator { public: t_py_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("new_style"); gen_newstyle_ = (iter != parsed_options.end()); iter = parsed_options.find("slots"); gen_slots_ = (iter != parsed_options.end()); iter = parsed_options.find("dynamic"); gen_dynamic_ = (iter != parsed_options.end()); if (gen_dynamic_) { gen_newstyle_ = 0; // dynamic is newstyle gen_dynbaseclass_ = "TBase"; gen_dynbaseclass_exc_ = "TExceptionBase"; import_dynbase_ = "from thrift.protocol.TBase import TBase, TExceptionBase\n"; } iter = parsed_options.find("dynbase"); if (iter != parsed_options.end()) { gen_dynbase_ = true; gen_dynbaseclass_ = (iter->second); } iter = parsed_options.find("dynexc"); if (iter != parsed_options.end()) { gen_dynbaseclass_exc_ = (iter->second); } iter = parsed_options.find("dynimport"); if (iter != parsed_options.end()) { gen_dynbase_ = true; import_dynbase_ = (iter->second); } iter = parsed_options.find("twisted"); gen_twisted_ = (iter != parsed_options.end()); iter = parsed_options.find("tornado"); gen_tornado_ = (iter != parsed_options.end()); if (gen_twisted_ && gen_tornado_) { throw "at most one of 'twisted' and 'tornado' are allowed"; } iter = parsed_options.find("utf8strings"); gen_utf8strings_ = (iter != parsed_options.end()); 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"; } } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false); void generate_py_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_py_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_py_struct_required_validator(std::ofstream& 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::ofstream &out, t_field* tfield, std::string prefix="", bool inclass=false); void generate_deserialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream &out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream &out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream &out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream &out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream &out, t_map* tmap, std::string kiter, std::string viter); void generate_serialize_set_element (std::ofstream &out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream &out, t_list* tlist, std::string iter); void generate_python_docstring (std::ofstream& out, t_struct* tstruct); void generate_python_docstring (std::ofstream& out, t_function* tfunction); void generate_python_docstring (std::ofstream& out, t_doc* tdoc, t_struct* tstruct, const char* subheader); void generate_python_docstring (std::ofstream& out, t_doc* tdoc); /** * a type for specifying to function_signature what type of Tornado callback * parameter to add */ enum tornado_callback_t { NONE = 0, MANDATORY_FOR_ONEWAY_ELSE_NONE = 1, OPTIONAL_FOR_ONEWAY_ELSE_MANDATORY = 2, }; /** * Helper rendering functions */ std::string py_autogen_comment(); std::string py_imports(); std::string render_includes(); std::string render_fastbinary_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, tornado_callback_t callback=NONE); std::string argument_list(t_struct* tstruct, std::vector *pre=NULL, std::vector *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) { 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 real_module; } 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_exc_; std::string import_dynbase_; bool gen_slots_; std::string copy_options_; /** * 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_; /** * File streams */ std::ofstream f_types_; std::ofstream f_consts_; std::ofstream f_service_; std::string package_dir_; std::string module_; }; /** * 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 f_init; f_init.open(f_init_name.c_str()); f_init << "__all__ = ['ttypes', 'constants'"; vector services = program_->get_services(); vector::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 << render_fastbinary_includes() << endl << endl; f_consts_ << py_autogen_comment() << endl << py_imports() << endl << "from ttypes import *" << endl << endl; } /** * Renders all the imports necessary for including another Thrift program */ string t_py_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; for (size_t i = 0; i < includes.size(); ++i) { result += "import " + get_real_py_module(includes[i], gen_twisted_) + ".ttypes\n"; } if (includes.size() > 0) { result += "\n"; } return result; } /** * Renders all the imports necessary to use the accelerated TBinaryProtocol */ string t_py_generator::render_fastbinary_includes() { string hdr = ""; if (gen_dynamic_) { hdr += std::string(import_dynbase_); } else { hdr += "from thrift.transport import TTransport\n" "from thrift.protocol import TBinaryProtocol, TProtocol\n" "try:\n" " from thrift.protocol import fastbinary\n" "except:\n" " fastbinary = None\n"; } return hdr; } /** * Autogen'd comment */ string t_py_generator::py_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" + "# options string: " + copy_options_ + "\n" + "#\n"; } /** * Prints standard thrift imports */ string t_py_generator::py_imports() { return string("from thrift.Thrift import TType, TMessageType, TException, TApplicationException"); } /** * Closes the type files */ void t_py_generator::close_generator() { // 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_ << "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 constants = tenum->get_constants(); vector::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() << endl; } /** * 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: 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_BYTE: 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_name(type) << "(**{" << endl; indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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 << "{" << endl; indent_up(); const map& val = value->get_map(); map::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(); } if (type->is_set()) { out << "set("; } out << "[" << endl; indent_up(); const vector& val = value->get_list(); vector::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) << "]"; if (type->is_set()) { out << ")"; } } else { throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); } return out.str(); } /** * Generates a python struct */ void t_py_generator::generate_struct(t_struct* tstruct) { generate_py_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_py_generator::generate_xception(t_struct* txception) { generate_py_struct(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); } /** * Generates a struct definition for a thrift data type. * * @param tstruct The struct definition */ void t_py_generator::generate_py_struct_definition(ofstream& out, t_struct* tstruct, bool is_exception, bool is_result) { (void) is_result; const vector& members = tstruct->get_members(); const vector& sorted_members = tstruct->get_sorted_members(); vector::const_iterator m_iter; out << std::endl << "class " << tstruct->get_name(); if (is_exception) { if (gen_dynamic_) { out << "(" << gen_dynbaseclass_exc_ << ")"; } else { out << "(TException)"; } } else { if (gen_newstyle_) { out << "(object)"; } else if (gen_dynamic_) { out << "(" << gen_dynbaseclass_ << ")"; } } 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 (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) { indent(out) << "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 << endl; } else { indent(out) << "thrift_spec = None" << endl; } if (members.size() > 0) { out << indent() << "def __init__(self,"; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { // This fills in default values, as opposed to nulls 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(out) << " " << (*m_iter)->get_name() << " = " << render_field_default_value(*m_iter) << endl; } indent(out) << "self." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << endl; } indent_down(); out << endl; } if (!gen_dynamic_) { 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 << indent() << "def __str__(self):" << endl << indent() << " return repr(self)" << endl << endl; } if (!gen_slots_) { // Printing utilities so that on the command line thrift // structs look pretty like dictionaries out << indent() << "def __repr__(self):" << endl << indent() << " L = ['%s=%r' % (key, value)" << endl << indent() << " for key, value in self.__dict__.iteritems()]" << endl << indent() << " return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl << endl; // 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_) { // no base class available to implement __eq__ and __repr__ and __ne__ for us // so we must provide one that uses __slots__ out << indent() << "def __repr__(self):" << endl << 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; // Equality method that compares each attribute by value and type, walking __slots__ out << indent() << "def __eq__(self, other):" << endl << indent() << " if not isinstance(other, self.__class__):" << endl << indent() << " return False" << endl << indent() << " for attr in self.__slots__:" << endl << indent() << " my_val = getattr(self, attr)" << endl << indent() << " other_val = getattr(other, attr)" << endl << indent() << " if my_val != other_val:" << endl << indent() << " return False" << endl << indent() << " return True" << endl << endl; out << indent() << "def __ne__(self, other):" << endl << indent() << " return not (self == other)" << endl << endl; } indent_down(); } /** * Generates the read method for a struct */ void t_py_generator::generate_py_struct_reader(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; indent(out) << "def read(self, iprot):" << endl; indent_up(); indent(out) << "if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated " "and isinstance(iprot.trans, TTransport.CReadableTransport) " "and self.thrift_spec is not None " "and fastbinary is not None:" << endl; indent_up(); indent(out) << "fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec))" << endl; indent(out) << "return" << endl; indent_down(); indent(out) << "iprot.readStructBegin()" << 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(); generate_deserialize_field(out, *f_iter, "self."); indent_down(); out << indent() << "else:" << endl << indent() << " iprot.skip(ftype)" << endl; indent_down(); } // In the default case we skip the field out << indent() << "else:" << endl << indent() << " iprot.skip(ftype)" << endl; // Read field end marker indent(out) << "iprot.readFieldEnd()" << endl; indent_down(); indent(out) << "iprot.readStructEnd()" << endl; indent_down(); out << endl; } void t_py_generator::generate_py_struct_writer(ofstream& out, t_struct* tstruct) { string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; indent(out) << "def write(self, oprot):" << endl; indent_up(); indent(out) << "if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated " "and self.thrift_spec is not None " "and fastbinary is not None:" << endl; indent_up(); indent(out) << "oprot.trans.write(fastbinary.encode_binary(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); out << endl; } void t_py_generator::generate_py_struct_required_validator(ofstream& out, t_struct* tstruct) { indent(out) << "def validate(self):" << endl; indent_up(); const vector& fields = tstruct->get_members(); if (fields.size() > 0) { vector::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) << " raise TProtocol.TProtocolException(message='Required field " << field->get_name() << " is unset!')" << endl; } } } indent(out) << "return" << endl << 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_) << "." << tservice->get_extends()->get_name() << endl; } f_service_ << "from ttypes import *" << endl << "from thrift.Thrift import TProcessor" << endl << render_fastbinary_includes() << endl; if (gen_twisted_) { f_service_ << "from zope.interface import Interface, implements" << endl << "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 stack_context" << endl; } f_service_ << endl; // Generate the three main parts of the service (well, two for now in PHP) 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_.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 functions = tservice->get_functions(); vector::iterator f_iter; f_service_ << "# 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_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& fields = xs->get_members(); vector::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, true); } } /** * 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_twisted_) { extends_if = "(Interface)"; } else if (gen_newstyle_ || gen_dynamic_ || gen_tornado_) { extends_if = "(object)"; } } f_service_ << "class Iface" << extends_if << ":" << endl; indent_up(); generate_python_docstring(f_service_, tservice); vector functions = tservice->get_functions(); if (functions.empty()) { f_service_ << indent() << "pass" << endl; } else { vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << indent() << "def " << function_signature(*f_iter, true, OPTIONAL_FOR_ONEWAY_ELSE_MANDATORY) << ":" << endl; indent_up(); generate_python_docstring(f_service_, (*f_iter)); f_service_ << indent() << "pass" << endl << endl; indent_down(); } } indent_down(); f_service_ << endl; } /** * 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_twisted_) { extends_client = "(" + extends + ".Client)"; } else { extends_client = extends + ".Client, "; } } else { if (gen_twisted_ && (gen_newstyle_ || gen_dynamic_)) { extends_client = "(object)"; } } if (gen_twisted_) { f_service_ << "class Client" << extends_client << ":" << endl << " implements(Iface)" << 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; } 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 << 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 << endl; } else { f_service_ << indent() << " self._iprot = self._oprot = iprot" << endl << indent() << " if oprot is not None:" << endl << indent() << " self._oprot = oprot" << endl << indent() << " self._seqid = 0" << endl << endl; } } else { if (gen_twisted_) { f_service_ << indent() << " " << extends << ".Client.__init__(self, transport, oprot_factory)" << endl << endl; } else if (gen_tornado_) { f_service_ << indent() << " " << extends << ".Client.__init__(self, transport, iprot_factory, oprot_factory)" << endl << endl; } else { f_service_ << indent() << " " << extends << ".Client.__init__(self, iprot, oprot)" << endl << endl; } } if (gen_tornado_ && extends.empty()) { f_service_ << indent() << "@gen.engine" << endl << indent() << "def recv_dispatch(self):" << endl << indent() << " \"\"\"read a response from the wire. schedule exactly one per send that" << endl << indent() << " expects a response, but it doesn't matter which callee gets which" << endl << indent() << " response; they're dispatched here properly\"\"\"" << endl << endl << indent() << " # wait for a frame header" << endl << indent() << " frame = yield gen.Task(self._transport.readFrame)" << 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() << " method(iprot, mtype, rseqid)" << endl << endl; } // Generate client method implementations vector functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::const_iterator fld_iter; string funname = (*f_iter)->get_name(); // Open function indent(f_service_) << "def " << function_signature(*f_iter, false, OPTIONAL_FOR_ONEWAY_ELSE_MANDATORY) << ":" << endl; indent_up(); generate_python_docstring(f_service_, (*f_iter)); if (gen_twisted_) { indent(f_service_) << "self._seqid += 1" << endl; if (!(*f_iter)->is_oneway()) { indent(f_service_) << "d = self._reqs[self._seqid] = defer.Deferred()" << endl; } } else if (gen_tornado_) { indent(f_service_) << "self._seqid += 1" << endl; if (!(*f_iter)->is_oneway()) { indent(f_service_) << "self._reqs[self._seqid] = callback" << endl; } } indent(f_service_) << "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(); } if (gen_tornado_ && (*f_iter)->is_oneway()) { if (first) { first = false; } else { f_service_ << ", "; } f_service_ << "callback"; } f_service_ << ")" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent(); if (gen_twisted_) { f_service_ << "return d" << endl; } else if (gen_tornado_) { f_service_ << "self.recv_dispatch()" << endl; } else { if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "self.recv_" << funname << "()" << endl; } } else { if (gen_twisted_) { f_service_ << indent() << "return defer.succeed(None)" << endl; } } indent_down(); f_service_ << endl; indent(f_service_) << "def send_" << function_signature(*f_iter, false, MANDATORY_FOR_ONEWAY_ELSE_NONE) << ":" << endl; indent_up(); std::string argsname = (*f_iter)->get_name() + "_args"; // 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() << "', TMessageType.CALL, self._seqid)" << endl; } else { f_service_ << indent() << "self._oprot.writeMessageBegin('" << (*f_iter)->get_name() << "', TMessageType.CALL, 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_) { f_service_ << indent() << "args.write(oprot)" << endl << indent() << "oprot.writeMessageEnd()" << endl << indent() << "oprot.trans.flush()" << endl; } else if (gen_tornado_) { f_service_ << indent() << "args.write(oprot)" << endl << indent() << "oprot.writeMessageEnd()" << endl; if ((*f_iter)->is_oneway()) { // send_* carry the callback so you can block on the write's flush // (rather than on receipt of the response) f_service_ << indent() << "oprot.trans.flush(callback=callback)" << endl; } else { f_service_ << 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_) { f_service_ << indent() << "callback = self._reqs.pop(rseqid)" << endl; } else { f_service_ << indent() << "(fname, mtype, rseqid) = self._iprot.readMessageBegin()" << endl; } f_service_ << indent() << "if mtype == TMessageType.EXCEPTION:" << endl << indent() << " x = TApplicationException()" << endl; if (gen_twisted_) { f_service_ << indent() << " x.read(iprot)" << endl << indent() << " iprot.readMessageEnd()" << endl << indent() << " return d.errback(x)" << endl << indent() << "result = " << resultname << "()" << endl << indent() << "result.read(iprot)" << endl << indent() << "iprot.readMessageEnd()" << endl; } else if (gen_tornado_) { f_service_ << indent() << " x.read(iprot)" << endl << indent() << " iprot.readMessageEnd()" << endl << indent() << " callback(x)" << endl << indent() << " return" << endl << indent() << "result = " << resultname << "()" << endl << indent() << "result.read(iprot)" << endl << indent() << "iprot.readMessageEnd()" << endl; } else { f_service_ << indent() << " x.read(self._iprot)" << endl << indent() << " self._iprot.readMessageEnd()" << endl << indent() << " raise x" << endl << indent() << "result = " << resultname << "()" << endl << indent() << "result.read(self._iprot)" << endl << indent() << "self._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() << " return d.callback(result.success)" << endl; } else if (gen_tornado_) { f_service_ << indent() << " callback(result.success)" << endl << indent() << " return" << endl; } else { f_service_ << indent() << " return result.success" << endl; } } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << indent() << "if result." << (*x_iter)->get_name() << " is not None:" << endl; if (gen_twisted_) { f_service_ << indent() << " return d.errback(result." << (*x_iter)->get_name() << ")" << endl; } else if (gen_tornado_) { f_service_ << indent() << " callback(result." << (*x_iter)->get_name() << ")" << endl << indent() << " return" << endl; } else { f_service_ << indent() << " raise result." << (*x_iter)->get_name() << "" << 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 if (gen_tornado_) { f_service_ << indent() << "callback(None)" << endl << indent() << "return" << 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 if (gen_tornado_) { f_service_ << indent() << "callback(TApplicationException(TApplicationException.MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\"))" << endl << indent() << "return" << endl; } else { f_service_ << indent() << "raise TApplicationException(TApplicationException.MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; } } // Close function indent_down(); f_service_ << endl; } } indent_down(); f_service_ << endl; } /** * 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 functions = tservice->get_functions(); //Get all function from parents t_service* parent = tservice->get_extends(); while(parent != NULL) { vector p_functions = parent->get_functions(); functions.insert(functions.end(), p_functions.begin(), p_functions.end()); parent = parent->get_extends(); } vector::iterator f_iter; string f_remote_name = package_dir_+"/"+service_name_+"-remote"; ofstream 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 << "from urlparse import urlparse" << endl << "from thrift.transport import TTransport" << endl << "from thrift.transport import TSocket" << endl << "from thrift.transport import THttpClient" << endl << "from thrift.protocol 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 << " print ''" << endl << " print 'Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] function [arg1 [arg2...]]'" << endl << " print ''" << endl << " print 'Functions:'" << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_remote << " print ' " << (*f_iter)->get_returntype()->get_name() << " " << (*f_iter)->get_name() << "("; t_struct* arg_struct = (*f_iter)->get_arglist(); const std::vector& args = arg_struct->get_members(); vector::const_iterator a_iter; int num_args = args.size(); bool first = true; for (int 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 << " print ''" << endl << " sys.exit(0)" << endl << endl; f_remote << "pp = pprint.PrettyPrinter(indent = 2)" << endl << "host = 'localhost'" << endl << "port = 9090" << endl << "uri = ''" << endl << "framed = False" << endl << "http = False" << endl << "argi = 1" << endl << endl << "if sys.argv[argi] == '-h':" << endl << " parts = sys.argv[argi+1].split(':')" << endl << " host = parts[0]" << endl << " if len(parts) > 1:" << endl << " port = int(parts[1])" << endl << " argi += 2" << endl << endl << "if sys.argv[argi] == '-u':" << endl << " url = urlparse(sys.argv[argi+1])" << endl << " parts = url[1].split(':')" << endl << " host = parts[0]" << endl << " if len(parts) > 1:" << endl << " port = int(parts[1])" << endl << " else:" << endl << " port = 80" << endl << " uri = url[2]" << endl << " if url[4]:" << endl << " uri += '?%s' % url[4]" << endl << " http = True" << endl << " argi += 2" << endl << endl << "if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed':" << endl << " framed = True" << endl << " argi += 1" << endl << endl << "cmd = sys.argv[argi]" << endl << "args = sys.argv[argi+1:]" << endl << endl << "if http:" << endl << " transport = THttpClient.THttpClient(host, port, uri)" << endl << "else:" << endl << " socket = TSocket.TSocket(host, port)" << endl << " if framed:" << endl << " transport = TTransport.TFramedTransport(socket)" << endl << " else:" << endl << " transport = TTransport.TBufferedTransport(socket)" << endl << "protocol = TBinaryProtocol.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& args = arg_struct->get_members(); vector::const_iterator a_iter; int num_args = args.size(); f_remote << "if cmd == '" << (*f_iter)->get_name() << "':" << endl << " if len(args) != " << num_args << ":" << endl << " print '" << (*f_iter)->get_name() << " requires " << num_args << " args'" << endl << " sys.exit(1)" << endl << " pp.pprint(client." << (*f_iter)->get_name() << "("; for (int i = 0; i < num_args; ++i) { 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 << " print 'Unrecognized method %s' % cmd" << endl; f_remote << " 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 MINGW | 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 functions = tservice->get_functions(); vector::iterator f_iter; string extends = ""; string extends_processor = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_processor = extends + ".Processor, "; } // Generate the header portion if (gen_twisted_) { f_service_ << "class Processor(" << extends_processor << "TProcessor):" << endl << " implements(Iface)" << endl << 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_twisted_) { 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_twisted_) { 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; } indent_down(); f_service_ << endl; // Generate the server implementation if (gen_tornado_) { f_service_ << indent() << "@gen.engine" << endl << indent() << "def process(self, transport, iprot_factory, oprot, callback):" << endl; indent_up(); f_service_ << indent() << "# wait for a frame header" << endl << indent() << "frame = yield gen.Task(transport.readFrame)" << endl << indent() << "tr = TTransport.TMemoryBuffer(frame)" << endl << indent() << "iprot = iprot_factory.getProtocol(tr)" << endl << endl; } else { f_service_ << indent() << "def process(self, iprot, oprot):" << endl; indent_up(); } f_service_ << indent() << "(name, type, seqid) = iprot.readMessageBegin()" << endl; // TODO(mcslee): validate message // HOT: dictionary function lookup f_service_ << indent() << "if name not in self._processMap:" << endl << 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 if (gen_tornado_) { // nothing } else { f_service_ << indent() << " return" << endl; } f_service_ << indent() << "else:" << endl; if (gen_twisted_) { f_service_ << indent() << " return self._processMap[name](self, seqid, iprot, oprot)" << endl; } else if (gen_tornado_) { f_service_ << indent() << " yield gen.Task(self._processMap[name], self, seqid, iprot, oprot)" << endl << indent() << "callback()" << endl; } else { f_service_ << indent() << " 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(); 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(); f_service_ << endl; } /** * 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.engine" << endl << indent() << "def process_" << tfunction->get_name() << "(self, seqid, iprot, oprot, callback):" << 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& xceptions = xs->get_members(); vector::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& fields = arg_struct->get_members(); vector::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; // Shortcut out here for oneway functions if (tfunction->is_oneway()) { f_service_ << indent() << "return d" << endl; indent_down(); f_service_ << endl; return; } f_service_ << indent() << "d.addCallback(self.write_results_success_" << tfunction->get_name() << ", result, seqid, oprot)" << endl; if (xceptions.size() > 0) { f_service_ << indent() << "d.addErrback(self.write_results_exception_" << tfunction->get_name() << ", result, seqid, oprot)" << endl; } f_service_ << indent() << "return d" << endl; indent_down(); f_service_ << endl; indent(f_service_) << "def write_results_success_" << tfunction->get_name() << "(self, success, result, seqid, oprot):" << endl; indent_up(); f_service_ << indent() << "result.success = success" << endl << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name() << "\", TMessageType.REPLY, seqid)" << endl << indent() << "result.write(oprot)" << endl << indent() << "oprot.writeMessageEnd()" << endl << indent() << "oprot.trans.flush()" << endl; indent_down(); f_service_ << endl; // Try block for a function with exceptions if (!tfunction->is_oneway() && xceptions.size() > 0) { indent(f_service_) << "def write_results_exception_" << tfunction->get_name() << "(self, error, result, seqid, oprot):" << endl; indent_up(); f_service_ << indent() << "try:" << endl; // Kinda absurd f_service_ << indent() << " error.raiseException()" << endl; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << ", " << (*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(); } else { f_service_ << indent() << "pass" << 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; indent_down(); f_service_ << endl; } } else if (gen_tornado_) { if (!tfunction->is_oneway() && xceptions.size() > 0) { f_service_ << endl << indent() << "def handle_exception(xtype, value, traceback):" << endl; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << indent() << " if xtype == " << type_name((*x_iter)->get_type()) << ":" << endl; if (!tfunction->is_oneway()) { f_service_ << indent() << " result." << (*x_iter)->get_name() << " = value" << endl; } f_service_ << indent() << " return True" << endl; } f_service_ << endl << indent() << "with stack_context.ExceptionStackContext(handle_exception):" << endl; indent_up(); } // Generate the function call t_struct* arg_struct = tfunction->get_arglist(); const std::vector& fields = arg_struct->get_members(); vector::const_iterator f_iter; f_service_ << indent(); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { f_service_ << "result.success = "; } f_service_ << "yield gen.Task(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 (xceptions.size() > 0) { f_service_ << endl; } if (!tfunction->is_oneway() && xceptions.size() > 0) { indent_down(); } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { f_service_ << indent() << "callback()" << endl; indent_down(); f_service_ << endl; return; } 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 << indent() << "callback()" << endl; // Close function indent_down(); f_service_ << endl; } else { // py // 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& fields = arg_struct->get_members(); vector::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() << "except " << type_name((*x_iter)->get_type()) << ", " << (*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(); } else { f_service_ << indent() << "pass" << endl; } } } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { f_service_ << indent() << "return" << endl; indent_down(); f_service_ << endl; return; } 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; // Close function indent_down(); f_service_ << endl; } } /** * Deserializes a field of any type. */ void t_py_generator::generate_deserialize_field(ofstream &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 = 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 (((t_base_type*)type)->is_binary() || !gen_utf8strings_) { out << "readString();"; } else { out << "readString().decode('utf-8')"; } break; case t_base_type::TYPE_BOOL: out << "readBool();"; break; case t_base_type::TYPE_BYTE: 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()); } } /** * Generates an unserializer for a struct, calling read() */ void t_py_generator::generate_deserialize_struct(ofstream &out, t_struct* tstruct, string prefix) { 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(ofstream &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_byte, ktype); t_field fvtype(g_type_byte, vtype); t_field fetype(g_type_byte, 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 xrange(" << 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; } else if (ttype->is_set()) { indent(out) << "iprot.readSetEnd()" << endl; } else if (ttype->is_list()) { indent(out) << "iprot.readListEnd()" << endl; } } /** * Generates code to deserialize a map */ void t_py_generator::generate_deserialize_map_element(ofstream &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(ofstream &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(ofstream &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(ofstream &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 (((t_base_type*)type)->is_binary() || !gen_utf8strings_) { out << "writeString(" << name << ")"; } else { out << "writeString(" << name << ".encode('utf-8'))"; } break; case t_base_type::TYPE_BOOL: out << "writeBool(" << name << ")"; break; case t_base_type::TYPE_BYTE: 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_py_generator::generate_serialize_struct(ofstream &out, t_struct* tstruct, string prefix) { (void) tstruct; indent(out) << prefix << ".write(oprot)" << endl; } void t_py_generator::generate_serialize_container(ofstream &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(ofstream &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(ofstream &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(ofstream &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(ofstream& 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(ofstream& 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(ofstream& 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& fields = tstruct->get_members(); if (fields.size() > 0) { if (has_doc) { ss << endl; } has_doc = true; ss << subheader << ":\n"; vector::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(ofstream& 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 << "thrift_spec[" << tfield->get_key() << "][4]"; } 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, tornado_callback_t callback) { vector pre; vector post; string signature = tfunction->get_name() + "("; if (!(gen_twisted_ && interface)) { pre.push_back("self"); } if (gen_tornado_) { if (callback == NONE) { } else if (callback == MANDATORY_FOR_ONEWAY_ELSE_NONE) { if (tfunction->is_oneway()) { // Tornado send_* carry the callback so you can block on the write's flush // (rather than on receipt of the response) post.push_back("callback"); } } else if (callback == OPTIONAL_FOR_ONEWAY_ELSE_MANDATORY) { if (tfunction->is_oneway()) { post.push_back("callback=None"); } else { post.push_back("callback"); } } } signature += argument_list(tfunction->get_arglist(), &pre, &post) + ")"; return signature; } /** * Renders a field list */ string t_py_generator::argument_list(t_struct* tstruct, vector *pre, vector *post) { string result = ""; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; vector::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) { t_program* program = ttype->get_program(); if (ttype->is_service()) { return get_real_py_module(program, gen_twisted_) + "." + ttype->get_name(); } if (program != NULL && program != program_) { return get_real_py_module(program, gen_twisted_) + ".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_BYTE: 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_base_type() || ttype->is_enum()) { return "None"; } 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(); } THRIFT_REGISTER_GENERATOR(py, "Python", " new_style: Generate new-style classes.\n" \ " twisted: Generate Twisted-friendly RPC services.\n" \ " tornado: Generate code for use with Tornado.\n" \ " utf8strings: Encode/decode strings using utf8 in the generated code.\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" \ " 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") thrift-compiler_0.9.1/cpp/src/generate/t_generator.h0000644000175000017500000001624312203157755023341 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include "parse/t_program.h" #include "globals.h" #include "t_generator_registry.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) { 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::ofstream& out, const std::string& comment_start, const std::string& line_prefix, const std::string& contents, const std::string& comment_end); /** * 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()); } protected: /** * Optional methods that may be imlemented 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 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_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(); } /** * Indentation level modifiers */ void indent_up(){ ++indent_; } void indent_down() { --indent_; } /** * Indentation print function */ std::string indent() { std::string ind = ""; int i; for (i = 0; i < indent_; ++i) { ind += " "; } 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; } std::string lowercase(std::string in) { for (size_t i = 0; i < in.size(); ++i) { in[i] = tolower(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(); } public: /** * Get the true type behind a series of typedefs. */ 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 escape_; private: /** * Current code indentation level */ int indent_; /** * Temporary variable counter, for making unique variable names */ int tmp_; }; #endif thrift-compiler_0.9.1/cpp/src/generate/t_cpp_generator.cc0000644000175000017500000047142412203157755024347 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include "platform.h" #include "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& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("pure_enums"); gen_pure_enums_ = (iter != parsed_options.end()); iter = parsed_options.find("dense"); gen_dense_ = (iter != parsed_options.end()); iter = parsed_options.find("include_prefix"); use_include_prefix_ = (iter != parsed_options.end()); iter = parsed_options.find("cob_style"); gen_cob_style_ = (iter != parsed_options.end()); iter = parsed_options.find("no_client_completion"); gen_no_client_completion_ = (iter != parsed_options.end()); iter = parsed_options.find("templates"); gen_templates_ = (iter != parsed_options.end()); gen_templates_only_ = (iter != parsed_options.end() && iter->second == "only"); out_dir_base_ = "gen-cpp"; } /** * Init and close methods */ void init_generator(); void close_generator(); void generate_consts(std::vector consts); /** * Program-level generation functions */ void generate_typedef(t_typedef* ttypedef); void generate_enum(t_enum* tenum); void generate_struct(t_struct* tstruct) { generate_cpp_struct(tstruct, false); } void generate_xception(t_struct* txception) { generate_cpp_struct(txception, true); } void generate_cpp_struct(t_struct* tstruct, bool is_exception); void generate_service(t_service* tservice); void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); std::string render_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); void generate_struct_definition (std::ofstream& out, t_struct* tstruct, bool is_exception=false, bool pointers=false, bool read=true, bool write=true, bool swap=false); void generate_struct_fingerprint (std::ofstream& out, t_struct* tstruct, bool is_definition); void generate_struct_reader (std::ofstream& out, t_struct* tstruct, bool pointers=false); void generate_struct_writer (std::ofstream& out, t_struct* tstruct, bool pointers=false); void generate_struct_result_writer (std::ofstream& out, t_struct* tstruct, bool pointers=false); void generate_struct_swap (std::ofstream& 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::ofstream& out, t_field* tfield, std::string prefix="", std::string suffix=""); void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream& out, t_list* tlist, std::string prefix, bool push_back, std::string index); void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix="", std::string suffix=""); void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter); void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream& 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); std::string local_reflection_name(const char*, t_type* ttype, bool external=false); void generate_enum_constant_list(std::ofstream& f, const vector& constants, const char* prefix, const char* suffix, bool include_values); // These handles checking gen_dense_ and checking for duplicates. void generate_local_reflection(std::ofstream& out, t_type* ttype, bool is_definition); void generate_local_reflection_pointer(std::ofstream& out, t_type* ttype); 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; } 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 local reflection metadata for TDenseProtocol. */ bool gen_dense_; /** * 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 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_; /** * 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. */ std::ofstream f_types_; std::ofstream f_types_impl_; std::ofstream f_types_tcc_; std::ofstream f_header_; std::ofstream f_service_; std::ofstream f_service_tcc_; /** * When generating local reflections, make sure we don't generate duplicates. */ std::set reflected_fingerprints_; // 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.c_str()); 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 " << endl << "#include " << endl << "#include " << endl << "#include " << endl << endl; // Include C++xx compatibility header f_types_ << "#include " << endl; // Include other Thrift includes const vector& includes = program_->get_includes(); for (size_t i = 0; i < includes.size(); ++i) { f_types_ << "#include \"" << get_include_prefix(*(includes[i])) << includes[i]->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(*(includes[i])) << includes[i]->get_name() << "_types.tcc\"" << endl; } f_types_ << endl; // Include custom headers const vector& cpp_includes = program_->get_cpp_includes(); for (size_t i = 0; i < cpp_includes.size(); ++i) { if (cpp_includes[i][0] == '<') { f_types_ << "#include " << cpp_includes[i] << endl; } else { f_types_ << "#include \"" << cpp_includes[i] << "\"" << 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; // If we are generating local reflection metadata, we need to include // the definition of TypeSpec. if (gen_dense_) { f_types_impl_ << "#include " << endl << endl; } // The swap() code needs for std::swap() f_types_impl_ << "#include " << 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::ofstream& f, const vector& constants, const char* prefix, const char* suffix, bool include_values) { f << " {" << endl; indent_up(); vector::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 && (*c_iter)->has_value()) { 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 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 _" << tenum->get_name() << "_VALUES_TO_NAMES;" << endl << endl; f_types_impl_ << indent() << "const std::map _" << 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_local_reflection(f_types_, tenum, false); generate_local_reflection(f_types_impl_, tenum, true); } /** * Generates a class that holds all the constants. */ void t_cpp_generator::generate_consts(std::vector consts) { string f_consts_name = get_out_dir()+program_name_+"_constants.h"; ofstream f_consts; f_consts.open(f_consts_name.c_str()); string f_consts_impl_name = get_out_dir()+program_name_+"_constants.cpp"; ofstream f_consts_impl; f_consts_impl.open(f_consts_impl_name.c_str()); // 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::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; } /** * 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(ofstream& 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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& val = value->get_list(); vector::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(ofstream& 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_BYTE: 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 << 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 { string t = tmp("tmp"); indent(out) << type_name(type) << " " << t << ";" << endl; print_const_value(out, t, type, value); render << t; } return render.str(); } /** * 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_definition(f_types_, tstruct, is_exception, false, true, true, true); generate_struct_fingerprint(f_types_impl_, tstruct, true); generate_local_reflection(f_types_, tstruct, false); generate_local_reflection(f_types_impl_, tstruct, true); generate_local_reflection_pointer(f_types_impl_, tstruct); std::ofstream& 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); } /** * Writes the struct definition into the header file * * @param out Output stream * @param tstruct The struct */ void t_cpp_generator::generate_struct_definition(ofstream& out, t_struct* tstruct, bool is_exception, bool pointers, bool read, bool write, bool swap) { string extends = ""; if (is_exception) { extends = " : public ::apache::thrift::TException"; } // Get members vector::const_iterator m_iter; const vector& 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() << ";" << 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(); // Put the fingerprint up top for all to see. generate_struct_fingerprint(out, tstruct, false); if (!pointers) { // 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()) { string dval; if (t->is_enum()) { dval += "(" + type_name(t) + ")"; } dval += t->is_string() ? "" : "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() << "() throw() {}" << endl << endl; } // Pointer to this structure's reflection local typespec. if (gen_dense_) { indent(out) << "static ::apache::thrift::reflection::local::TypeSpec* local_reflection;" << endl << 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; } out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(" << type_name((*m_iter)->get_type(), false, true); out << " val) {" << endl << indent() << indent() << (*m_iter)->get_name() << " = val;" << endl; // 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; } out << endl; if (!pointers) { // 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 " << 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 " << endl << indent() << "uint32_t write(Protocol_* oprot) const;" << endl; } else { out << indent() << "uint32_t write(" << "::apache::thrift::protocol::TProtocol* oprot) const;" << endl; } } 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; } } /** * Writes the fingerprint of a struct to either the header or implementation. * * @param out Output stream * @param tstruct The struct */ void t_cpp_generator::generate_struct_fingerprint(ofstream& out, t_struct* tstruct, bool is_definition) { string stat, nspace, comment; if (is_definition) { stat = ""; nspace = tstruct->get_name() + "::"; comment = " "; } else { stat = "static "; nspace = ""; comment = "; // "; } if (tstruct->has_fingerprint()) { out << indent() << stat << "const char* " << nspace << "ascii_fingerprint" << comment << "= \"" << tstruct->get_ascii_fingerprint() << "\";" << endl << indent() << stat << "const uint8_t " << nspace << "binary_fingerprint[" << t_type::fingerprint_len << "]" << comment << "= {"; const char* comma = ""; for (int i = 0; i < t_type::fingerprint_len; i++) { out << comma << "0x" << t_struct::byte_to_hex(tstruct->get_binary_fingerprint()[i]); comma = ","; } out << "};" << endl << endl; } } /** * Writes the local reflection of a type (either declaration or definition). */ void t_cpp_generator::generate_local_reflection(std::ofstream& out, t_type* ttype, bool is_definition) { if (!gen_dense_) { return; } ttype = get_true_type(ttype); assert(ttype->has_fingerprint()); string key = ttype->get_ascii_fingerprint() + (is_definition ? "-defn" : "-decl"); // Note that we have generated this fingerprint. If we already did, bail out. if (!reflected_fingerprints_.insert(key).second) { return; } // Let each program handle its own structures. if (ttype->get_program() != NULL && ttype->get_program() != program_) { return; } // Do dependencies. if (ttype->is_list()) { generate_local_reflection(out, ((t_list*)ttype)->get_elem_type(), is_definition); } else if (ttype->is_set()) { generate_local_reflection(out, ((t_set*)ttype)->get_elem_type(), is_definition); } else if (ttype->is_map()) { generate_local_reflection(out, ((t_map*)ttype)->get_key_type(), is_definition); generate_local_reflection(out, ((t_map*)ttype)->get_val_type(), is_definition); } else if (ttype->is_struct() || ttype->is_xception()) { // Hacky hacky. For efficiency and convenience, we need a dummy "T_STOP" // type at the end of our typespec array. Unfortunately, there is no // T_STOP type, so we use the global void type, and special case it when // generating its typespec. const vector& members = ((t_struct*)ttype)->get_sorted_members(); vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { generate_local_reflection(out, (**m_iter).get_type(), is_definition); } generate_local_reflection(out, g_type_void, is_definition); // For definitions of structures, do the arrays of metas and field specs also. if (is_definition) { out << indent() << "::apache::thrift::reflection::local::FieldMeta" << endl << indent() << local_reflection_name("metas", ttype) <<"[] = {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { indent(out) << "{ " << (*m_iter)->get_key() << ", " << (((*m_iter)->get_req() == t_field::T_OPTIONAL) ? "true" : "false") << " }," << endl; } // Zero for the T_STOP marker. indent(out) << "{ 0, false }" << endl << "};" << endl; indent_down(); out << indent() << "::apache::thrift::reflection::local::TypeSpec*" << endl << indent() << local_reflection_name("specs", ttype) <<"[] = {" << endl; indent_up(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { indent(out) << "&" << local_reflection_name("typespec", (*m_iter)->get_type(), true) << "," << endl; } indent(out) << "&" << local_reflection_name("typespec", g_type_void) << "," << endl; indent_down(); indent(out) << "};" << endl; } } out << indent() << "// " << ttype->get_fingerprint_material() << endl << indent() << (is_definition ? "" : "extern ") << "::apache::thrift::reflection::local::TypeSpec" << endl << local_reflection_name("typespec", ttype) << (is_definition ? "(" : ";") << endl; if (!is_definition) { out << endl; return; } indent_up(); if (ttype->is_void()) { indent(out) << "::apache::thrift::protocol::T_STOP"; } else { indent(out) << type_to_enum(ttype); } if (ttype->is_struct()) { out << "," << endl << indent() << type_name(ttype) << "::binary_fingerprint," << endl << indent() << local_reflection_name("metas", ttype) << "," << endl << indent() << local_reflection_name("specs", ttype); } else if (ttype->is_list()) { out << "," << endl << indent() << "&" << local_reflection_name("typespec", ((t_list*)ttype)->get_elem_type(), true) << "," << endl << indent() << "NULL"; } else if (ttype->is_set()) { out << "," << endl << indent() << "&" << local_reflection_name("typespec", ((t_set*)ttype)->get_elem_type(), true) << "," << endl << indent() << "NULL"; } else if (ttype->is_map()) { out << "," << endl << indent() << "&" << local_reflection_name("typespec", ((t_map*)ttype)->get_key_type(), true) << "," << endl << indent() << "&" << local_reflection_name("typespec", ((t_map*)ttype)->get_val_type(), true); } out << ");" << endl << endl; indent_down(); } /** * Writes the structure's static pointer to its local reflection typespec * into the implementation file. */ void t_cpp_generator::generate_local_reflection_pointer(std::ofstream& out, t_type* ttype) { if (!gen_dense_) { return; } indent(out) << "::apache::thrift::reflection::local::TypeSpec* " << ttype->get_name() << "::local_reflection = " << endl << indent() << " &" << local_reflection_name("typespec", ttype) << ";" << endl << 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(ofstream& out, t_struct* tstruct, bool pointers) { if (gen_templates_) { out << indent() << "template " << 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& fields = tstruct->get_members(); vector::const_iterator f_iter; // Declare stack tmp variables out << 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(ofstream& out, t_struct* tstruct, bool pointers) { string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; if (gen_templates_) { out << indent() << "template " << 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) << "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(ofstream& out, t_struct* tstruct, bool pointers) { string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; if (gen_templates_) { out << indent() << "template " << 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(ofstream& 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& fields = tstruct->get_members(); for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_field *tfield = *f_iter; 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; } /** * 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 " << endl << // TMemoryBuffer "#include " << endl << "namespace apache { namespace thrift { namespace async {" << endl << "class TAsyncChannel;" << endl << "}}}" << endl; } f_header_ << "#include " << endl; if (gen_cob_style_) { f_header_ << "#include " << 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; // 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_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"); generate_service_async_skeleton(tservice); } // 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 functions = tservice->get_functions(); vector::iterator f_iter; std::ofstream& 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_definition(f_header_, 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_definition(f_header_, ts, false, true, false, true); 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 " << 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"; } } if (style == "CobCl" && gen_templates_) { f_header_ << "template " << endl; } f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl; indent_up(); f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl; vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++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 boost::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() << "boost::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 functions = tservice->get_functions(); vector::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& fields = tfunction->get_arglist()->get_members(); vector::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 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 " << 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 << "using boost::shared_ptr;" << 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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 >"; // 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 >::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(boost::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& args = arglist->get_members(); vector::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::ofstream& 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 \n"; short_suffix = "T"; template_suffix = "T"; protocol_type = "Protocol_"; _this = "this->"; } else { protocol_type = "::apache::thrift::protocol::TProtocol"; } string prot_ptr = "boost::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 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) :" << endl; if (extends.empty()) { f_header_ << indent() << " piprot_(prot)," << endl << indent() << " poprot_(prot) {" << endl << indent() << " iprot_ = prot.get();" << endl << indent() << " oprot_ = prot.get();" << endl << indent() << "}" << endl; } else { f_header_ << indent() << " " << extends << style << client_suffix << "(prot, prot) {}" << endl; } f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr << " iprot, " << prot_ptr << " oprot) :" << endl; if (extends.empty()) { f_header_ << indent() << " piprot_(iprot)," << endl << indent() << " poprot_(oprot) {" << endl << indent() << " iprot_ = iprot.get();" << endl << indent() << " oprot_ = oprot.get();" << endl << indent() << "}" << endl; } else { f_header_ << indent() << " " << extends << style << client_suffix << "(iprot, oprot) {}" << endl; } // Generate getters for the protocols. // Note that these are not currently templated for simplicity. // TODO(simpkins): should they be templated? f_header_ << indent() << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {" << endl << indent() << " return " << _this << "piprot_;" << endl << indent() << "}" << endl; f_header_ << indent() << "boost::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 << "(" << "boost::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_(boost::dynamic_pointer_cast(" << "protocolFactory->getProtocol(itrans_)))," << endl << indent() << " poprot_(boost::dynamic_pointer_cast(" << "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() << "boost::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 functions = tservice->get_functions(); vector::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. 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()) { 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() << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel_;" << endl << indent() << "boost::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> itrans_;" << endl << indent() << "boost::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; 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 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) << "send_" << funname << "("; // Get the struct of function call params t_struct* arg_struct = (*f_iter)->get_arglist(); // Declare the function arguments const vector& fields = arg_struct->get_members(); vector::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);" << endl; } else { out << "return recv_" << funname << "();" << endl; } } else { out << "recv_" << funname << "();" << endl; } } } else { if (!(*f_iter)->is_oneway()) { out << indent() << _this << "channel_->sendAndRecvMessage(" << "tcxx::bind(cob, this), " << _this << "otrans_.get(), " << _this << "itrans_.get());" << endl; } else { out << indent() << _this << "channel_->sendMessage(" << "tcxx::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) { // Function for sending t_function send_function(g_type_void, 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"; // Serialize the request out << indent() << "int32_t cseqid = 0;" << endl << indent() << _this << "oprot_->writeMessageBegin(\"" << (*f_iter)->get_name() << "\", ::apache::thrift::protocol::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; scope_down(out); out << endl; // Generate recv function only if not an oneway function if (!(*f_iter)->is_oneway()) { t_struct noargs(program_); t_function recv_function((*f_iter)->get_returntype(), string("recv_") + (*f_iter)->get_name(), &noargs); // 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 == "Cob" && !gen_no_client_completion_) { out << indent() << "bool completed = false;" << endl << endl << indent() << "try {"; indent_up(); } out << endl << indent() << _this << "iprot_->readMessageBegin(fname, mtype, rseqid);" << endl << 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; } 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; } 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 << indent() << " // _return pointer has now been filled" << endl; if (style == "Cob" && !gen_no_client_completion_) { out << indent() << " completed = true;" << endl << indent() << " completed__(true);" << 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; } out << indent() << " return _return;" << endl << indent() << "}" << endl; } } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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; } 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; } indent(out) << "return;" << endl; } else { if (style == "Cob" && !gen_no_client_completion_) { out << indent() << "completed = true;" << endl << indent() << "completed__(true);" << endl; } out << indent() << "throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << 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::ofstream& f_header_; std::ofstream& 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_ = "tcxx::function cob, "; finish_cob_decl_ = "tcxx::function, "; 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 \n"; template_suffix_ = ""; 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"; } } } void ProcessorGenerator::generate_class_definition() { // Generate the dispatch methods vector functions = service_->get_functions(); vector::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"; } } // 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() << "boost::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 " << "ProcessMap;" << endl; } else { f_header_ << indent() << "typedef std::map " << "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() << "(tcxx::function 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() << "(tcxx::function 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() << "(tcxx::function 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() << "(tcxx::function cob, int32_t seqid, " << "Protocol_* oprot, void* ctx, " << "::apache::thrift::TDelayedException* _throw);" << endl; } } } f_header_ << " public:" << endl << indent() << class_name_ << "(boost::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 functions = service_->get_functions(); vector::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 ::boost::shared_ptr< " << if_factory_name << " >& handlerFactory) :" << endl << indent() << " handlerFactory_(handlerFactory) {}" << endl << endl << indent() << "::boost::shared_ptr< ::apache::thrift::" << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > " << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);" << endl; f_header_ << endl << " protected:" << endl << indent() << "::boost::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() << "::boost::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() << "::boost::shared_ptr< " << if_name_ << " > handler(" << "handlerFactory_->getHandler(connInfo), cleanup);" << endl << indent() << "::boost::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; } /** * 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::ofstream& 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& fields = xs->get_members(); vector::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } generate_struct_definition(f_header_, &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_definition(f_header_, &result, false, true, true, gen_cob_style_); 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& fields = arg_struct->get_members(); vector::const_iterator f_iter; t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; string service_func_name = "\"" + tservice->get_name() + "." + tfunction->get_name() + "\""; std::ofstream& 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"; } // 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 " << 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 << "}"; } } } out << " catch (const 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; 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 " << endl; } out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::process_" << tfunction->get_name() << "(tcxx::function 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(iprot);" << endl << indent() << "Protocol_* _oprot = dynamic_cast(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& exn) {" << 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() << "(" << "tcxx::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 = ", tcxx::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)(tcxx::function " << "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)(tcxx::function " << "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() << "tcxx::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder << ")"; if (!xceptions.empty()) { out << ',' << endl << indent() << "tcxx::bind(throw_fn, this, cob, seqid, oprot, " << "ctx, tcxx::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 " << endl; } out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_" << tfunction->get_name() << "(tcxx::function 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(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 " << endl; } out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::throw_" << tfunction->get_name() << "(tcxx::function 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(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 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 " << endl << "#include " << endl << "#include " << endl << "#include " << 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 << "using boost::shared_ptr;" << 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 functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++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() << "shared_ptr<" << svcname << "Handler> handler(new " << svcname << "Handler());" << endl << indent() << "shared_ptr processor(new " << svcname << "Processor(handler));" << endl << indent() << "shared_ptr serverTransport(new TServerSocket(port));" << endl << indent() << "shared_ptr transportFactory(new TBufferedTransportFactory());" << endl << indent() << "shared_ptr 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(ofstream& 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); } 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 (((t_base_type*)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_BYTE: 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(ofstream& out, t_struct* tstruct, string prefix) { (void) tstruct; indent(out) << "xfer += " << prefix << ".read(iprot);" << endl; } void t_cpp_generator::generate_deserialize_container(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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); } 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 (((t_base_type*)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_BYTE: 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(ofstream& out, t_struct* tstruct, string prefix) { (void) tstruct; indent(out) << "xfer += " << prefix << ".write(oprot);" << endl; } void t_cpp_generator::generate_serialize_container(ofstream& 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(" << prefix << ".size()));" << endl; } else if (ttype->is_set()) { indent(out) << "xfer += oprot->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << "static_cast(" << prefix << ".size()));" << endl; } else if (ttype->is_list()) { indent(out) << "xfer += oprot->writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << "static_cast(" << 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(ofstream& 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(ofstream& 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(ofstream& 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 */ 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::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_BYTE: 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 (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_BYTE: 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"; } cob_type += "* client)"; } else if (style =="CobSv") { cob_type = (ttype->is_void() ? "()" : ("(" + type_name(ttype) + " const& _return)")); if (has_xceptions) { exn_cob = ", tcxx::function /* exn_cob */"; } } else { throw "UNKNOWN STYLE"; } return "void " + prefix + tfunction->get_name() + "(tcxx::function 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& fields = tstruct->get_members(); vector::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_BYTE: 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(); } /** * Returns the symbol name of the local reflection of a type. */ string t_cpp_generator::local_reflection_name(const char* prefix, t_type* ttype, bool external) { ttype = get_true_type(ttype); // We have to use the program name as part of the identifier because // if two thrift "programs" are compiled into one actual program // you would get a symbol collision if they both defined list. // trlo = Thrift Reflection LOcal. string prog; string name; string nspace; // TODO(dreiss): Would it be better to pregenerate the base types // and put them in Thrift.{h,cpp} ? if (ttype->is_base_type()) { prog = program_->get_name(); name = ttype->get_ascii_fingerprint(); } else if (ttype->is_enum()) { assert(ttype->get_program() != NULL); prog = ttype->get_program()->get_name(); name = ttype->get_ascii_fingerprint(); } else if (ttype->is_container()) { prog = program_->get_name(); name = ttype->get_ascii_fingerprint(); } else { assert(ttype->is_struct() || ttype->is_xception()); assert(ttype->get_program() != NULL); prog = ttype->get_program()->get_name(); name = ttype->get_ascii_fingerprint(); } if (external && ttype->get_program() != NULL && ttype->get_program() != program_) { nspace = namespace_prefix(ttype->get_program()->get_namespace("cpp")); } return nspace + "trlo_" + prefix + "_" + prog + "_" + 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" " templates: Generate templatized reader/writer methods.\n" " pure_enums: Generate pure enums instead of wrapper classes.\n" " dense: Generate type specifications for the dense protocol.\n" " include_prefix: Use full include paths in generated files.\n" ) thrift-compiler_0.9.1/cpp/src/generate/t_erl_generator.cc0000644000175000017500000007204012203157755024336 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "t_generator.h" #include "platform.h" #include "version.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& parsed_options, const std::string& option_string) : t_generator(program) { (void) parsed_options; (void) option_string; program_name_[0] = tolower(program_name_[0]); service_name_[0] = tolower(service_name_[0]); out_dir_base_ = "gen-erl"; } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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); /** * Service-level generation functions */ void generate_service_helpers (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 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 capitalize(std::string in) { in[0] = toupper(in[0]); return in; } std::string uncapitalize(std::string in) { in[0] = tolower(in[0]); return in; } static std::string comment(string in); private: bool has_default_value(t_field *); /** * 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_function(t_function* tfunction, std::string prefix=""); 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_; std::ofstream f_types_file_; std::ofstream f_types_hrl_file_; std::ofstream f_consts_; std::ostringstream f_service_; std::ofstream f_service_file_; std::ofstream f_service_hrl_; }; /** * 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; // types files string f_types_name = get_out_dir()+program_name_+"_types.erl"; string f_types_hrl_name = get_out_dir()+program_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_name_ + "_types"); f_types_file_ << erl_autogen_comment() << endl << "-module(" << program_name_ << "_types)." << endl << erl_imports() << endl; f_types_file_ << "-include(\"" << program_name_ << "_types.hrl\")." << endl << endl; f_types_hrl_file_ << render_includes() << endl; // consts file string f_consts_name = get_out_dir()+program_name_+"_constants.hrl"; f_consts_.open(f_consts_name.c_str()); f_consts_ << erl_autogen_comment() << endl << erl_imports() << endl << "-include(\"" << program_name_ << "_types.hrl\")." << endl << endl; } /** * Boilerplate at beginning and end of header files */ void t_erl_generator::hrl_header(ostream& out, string name) { out << "-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& includes = program_->get_includes(); string result = ""; for (size_t i = 0; i < includes.size(); ++i) { result += "-include(\"" + uncapitalize(includes[i]->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); f_types_file_ << "-export([" << export_types_lines_.str() << "])." << endl << endl; f_types_file_ << f_info_.str(); f_types_file_ << "struct_info('i am a dummy struct') -> undefined." << endl << endl; f_types_file_ << f_info_ext_.str(); f_types_file_ << "struct_info_ext('i am a dummy struct') -> undefined." << endl << endl; hrl_footer(f_types_hrl_file_, string("BOGUS")); f_types_file_.close(); f_types_hrl_file_.close(); f_consts_.close(); } /** * Generates a typedef. no op * * @param ttypedef The type definition */ void t_erl_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_erl_generator::generate_enum(t_enum* tenum) { vector constants = tenum->get_constants(); vector::iterator c_iter; 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_hrl_file_) << "-define(" << program_name_ << "_" << tenum->get_name() << "_" << name << ", " << value << ")."<< endl; } f_types_hrl_file_ << endl; } /** * Generate a constant value */ void t_erl_generator::generate_const(t_const* tconst) { t_type* type = tconst->get_type(); string name = capitalize(tconst->get_name()); t_const_value* value = tconst->get_value(); f_consts_ << "-define(" << program_name_ << "_" << 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_BYTE: 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 << "#" << uncapitalize(type->get_name()) << "{"; const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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(); out << "dict:from_list(["; map::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::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()) { t_type* etype; etype = ((t_list*)type)->get_elem_type(); out << "["; bool first = true; const vector& val = value->get_list(); vector::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); } out << "]"; } else { throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); } 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 "#" + uncapitalize(type->get_name()) + "{}"; } else if (type->is_map()) { 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_BYTE: 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 "#" + uncapitalize(type->get_name()) + "{}"; } else if (type->is_map()) { return "dict()"; } else if (type->is_set()) { return "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) { 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) { 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& members = tstruct->get_members(); for (vector::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 << endl; } /** * Generates the record field definition */ void t_erl_generator::generate_erl_struct_member(ostream & out, t_field * tmember) { out << uncapitalize(tmember->get_name()); if (has_default_value(tmember)) out << " = " << render_member_value(tmember); out << " :: " << render_member_type(tmember); } 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) { // somehow this point is reached before the constructor and it's not downcased yet // ...awesome service_name_[0] = tolower(service_name_[0]); 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(\"" << uncapitalize(tservice->get_extends()->get_name()) << "_thrift.hrl\"). % inherit " << endl; } f_service_hrl_ << "-include(\"" << 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); // 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(\"" << uncapitalize(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(); } /** * 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 functions = tservice->get_functions(); vector::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('i am a dummy struct') -> undefined." << 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 functions = tservice->get_functions(); vector::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_) << uncapitalize(tservice->get_extends()->get_name()) << "_thrift:function_info(Function, InfoType)." << endl; indent_down(); } else { // Use a special return code for nonexistent functions indent(f_service_) << "function_info(_Func, _Info) -> no_function." << 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 = "'" + 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_function(t_function* tfunction, string prefix) { export_types_string(prefix + tfunction->get_name(), 1 // This + ((tfunction->get_arglist())->get_members()).size() ); } 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) { export_string(prefix + tfunction->get_name(), 1 // This + ((tfunction->get_arglist())->get_members()).size() ); } /** * Renders a field list */ string t_erl_generator::argument_list(t_struct* tstruct) { string result = ""; const vector& fields = tstruct->get_members(); vector::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 = ""; string name = ttype->get_name(); if (ttype->is_struct() || ttype->is_xception() || ttype->is_service()) { name = uncapitalize(ttype->get_name()); } return 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_BYTE: 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(); } /** * 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_BYTE: 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(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 = uncapitalize(member->get_name()); string value = render_member_value(member); string requiredness = render_member_requiredness(member); buf << "{" << key << ", " << requiredness << ", " << type << ", '" << name << "'"<< ", " << value << "}"; } if ( ++i != end ) { buf << "," << endl << field_indent; } } buf << "]}" << endl; return buf.str(); } else { return "{struct, {'" + 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 uncapitalize(ttype->get_program()->get_name()) + "_types"; } THRIFT_REGISTER_GENERATOR(erl, "Erlang", "") thrift-compiler_0.9.1/cpp/src/generate/t_csharp_generator.cc0000644000175000017500000024547712203157755025054 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "platform.h" #include "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 class t_csharp_generator : public t_oop_generator { public: t_csharp_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("async"); async_ = (iter != parsed_options.end()); iter = parsed_options.find("asyncctp"); async_ctp_ = (iter != parsed_options.end()); if (async_ && async_ctp_) { throw "argument error: Cannot specify both async and asyncctp; they are incompatible."; } iter = parsed_options.find("nullable"); nullable_ = (iter != parsed_options.end()); iter = parsed_options.find("hashcode"); hashcode_ = (iter != parsed_options.end()); iter = parsed_options.find("union"); union_ = (iter != parsed_options.end()); iter = parsed_options.find("serial"); serialize_ = (iter != parsed_options.end()); if (serialize_) { wcf_namespace_ = iter->second; // since there can be only one namespace } iter = parsed_options.find("wcf"); wcf_ = (iter != parsed_options.end()); if (wcf_) { wcf_namespace_ = iter->second; } out_dir_base_ = "gen-csharp"; } void init_generator(); void close_generator(); void generate_consts(std::vector consts); void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_struct (t_struct* tstruct); void generate_union (t_struct* tunion); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); void generate_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset); void generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool includeIsset=true, std::string fieldPrefix = ""); bool print_const_value (std::ofstream& 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::ofstream& out, std::string name, t_type* type, t_const_value* value); void print_const_constructor(std::ofstream& out, std::vector consts); void print_const_def_value(std::ofstream& 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::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false); void generate_csharp_union_definition(std::ofstream& out, t_struct* tunion); void generate_csharp_union_class(std::ofstream& out, t_struct* tunion, t_field* tfield); void generate_csharp_wcffault(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_tostring(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_equals(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_hashcode(std::ofstream& out, t_struct* tstruct); void generate_csharp_union_reader(std::ofstream& out, t_struct* tunion); void generate_function_helpers(t_function* tfunction); 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* function); void generate_deserialize_field (std::ofstream& out, t_field* tfield, std::string prefix="", bool is_propertyless=false); void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream& out, t_list* list, std::string prefix=""); void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix="", bool is_element=false, bool is_propertyless=false); void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter, std::string map); void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream& out, t_list* tlist, std::string iter); void generate_csharp_doc (std::ofstream& out, t_field* field); void generate_csharp_doc (std::ofstream& out, t_doc* tdoc); void generate_csharp_doc (std::ofstream& out, t_function* tdoc); void generate_csharp_docstring_comment (std::ofstream &out, string contents); void start_csharp_namespace (std::ofstream& out); void end_csharp_namespace (std::ofstream& 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); std::string get_enum_class_name(t_type* type); std::string make_valid_csharp_identifier( std::string const & fromName); 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_; std::ofstream f_service_; std::string namespace_dir_; bool async_; bool async_ctp_; bool nullable_; bool union_; bool hashcode_; bool serialize_; bool wcf_; std::string wcf_namespace_; }; 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; } void t_csharp_generator::start_csharp_namespace(ofstream& out) { if (!namespace_name_.empty()) { out << "namespace " << namespace_name_ << "\n"; scope_up(out); } } void t_csharp_generator::end_csharp_namespace(ofstream& 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_||async_ctp_) ? "using System.Threading.Tasks;\n" : "") + "using Thrift;\n" + "using Thrift.Collections;\n" + (wcf_ ? "//using System.ServiceModel;\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 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 constants = tenum->get_constants(); vector::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 consts) { if (consts.empty()){ return; } string f_consts_name = namespace_dir_ + '/' + program_name_ + ".Constants.cs"; ofstream 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::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::ofstream& out, string name, t_type* type, t_const_value* value) { if (type->is_struct() || type->is_xception()) { const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::const_iterator v_iter; 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; } } 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& val = value->get_map(); map::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& val = value->get_list(); vector::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::ofstream& out, std::vector consts) { indent(out) << "static " << make_valid_csharp_identifier(program_name_).c_str() << "Constants()" << endl; scope_up(out); vector::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::ofstream& 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 ? "" : "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(ofstream& 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_BYTE: 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 " + 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 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(ofstream &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); 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 seperate "fault" for that } bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << " : "; if (is_exception) { out << "TException, "; } out << "TBase"; out << endl; scope_up(out); const vector& members = tstruct->get_members(); vector::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 << 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)) { indent(out) << "public bool " << (*m_iter)->get_name() << ";" << endl; } } indent_down(); indent(out) << "}" << endl << endl; } // We always want a default, no argument constructor for Reading indent(out) << "public " << 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." << (*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()) << " " << (*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)) << " = " << (*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); } if (!in_class) { end_csharp_namespace(out); } } void t_csharp_generator::generate_csharp_wcffault(ofstream& 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& members = tstruct->get_members(); vector::const_iterator m_iter; // make private members with public Properties for (m_iter = members.begin(); m_iter != members.end(); ++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(ofstream& out, t_struct* tstruct) { indent(out) << "public void Read (TProtocol iprot)" << endl; scope_up(out); const vector& fields = tstruct->get_members(); vector::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(); indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << endl; indent_down(); } } indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_writer(ofstream& out, t_struct* tstruct) { out << indent() << "public void Write(TProtocol oprot) {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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)); 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." << (*f_iter)->get_name() << ") {" << endl; indent_up(); } else { indent(out) << "if (__isset." << (*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; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_result_writer(ofstream& out, t_struct* tstruct) { indent(out) << "public void Write(TProtocol oprot) {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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." << (*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; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_tostring(ofstream& 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& fields = tstruct->get_members(); vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; 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 ? \"\" : "<< prop_name((*f_iter)) << ".ToString());" << endl; } else { indent(out) << "sb.Append(" << prop_name((*f_iter)) << ");" << endl; } } 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 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::ofstream& out, t_struct* tunion) { // Let's define the class first start_csharp_namespace(out); indent(out) << "public abstract class " << tunion->get_name() << " : TAbstractBase {" << endl; indent_up(); indent(out) << "public abstract void Write(TProtocol protocol);" << endl; indent(out) << "public readonly bool Isset;" << endl; indent(out) << "public abstract object Data { get; }" << endl; indent(out) << "protected " << tunion->get_name() << "(bool 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(false) {}" << 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& fields = tunion->get_members(); vector::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::ofstream& out, t_struct* tunion, t_field* tfield) { 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(true) {" << 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(); 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(); indent(out) << "}" << endl; indent_down(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_csharp_struct_equals(ofstream& 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& fields = tstruct->get_members(); vector::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." << (*f_iter)->get_name() << " == other.__isset." << (*f_iter)->get_name() << ") && ((!__isset." << (*f_iter)->get_name() << ") || ("; } t_type* ttype = (*f_iter)->get_type(); if (ttype->is_container()) { 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(ofstream& 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& fields = tstruct->get_members(); vector::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." << (*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 " << 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) { string extends = ""; string extends_iface = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_iface = " : " + extends + ".Iface"; } generate_csharp_doc(f_service_, tservice); if (wcf_) { indent(f_service_) << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << endl; } indent(f_service_) << "public interface Iface" << extends_iface << " {" << endl; indent_up(); vector functions = tservice->get_functions(); vector::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_) << "[OperationContract]" << endl; const std::vector& xceptions = (*f_iter)->get_xceptions()->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { indent(f_service_) << "[FaultContract(typeof(" + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl; } } indent(f_service_) << function_signature(*f_iter) << ";" << endl; 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_||async_ctp_) { indent(f_service_) << function_signature_async(*f_iter) << ";" << endl; } if (!async_) { indent(f_service_) << "#endif" << endl; } } indent_down(); f_service_ << indent() << "}" << endl << endl; } void t_csharp_generator::generate_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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; } // 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(); const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", "; f_service_ << (*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_||async_ctp_) { 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_); } if (async_) { f_service_ << "await Task.Run(() =>" << endl; } else { f_service_ << "await TaskEx.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; } // "Normal" Synchronous invoke generate_csharp_doc(f_service_, *f_iter); indent(f_service_) << "public " << function_signature(*f_iter) << endl; scope_up(f_service_); if (!async_) { indent(f_service_) << "#if !SILVERLIGHT" << endl; 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_ << (*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; indent(f_service_) << "#else" << endl; } // Silverlight synchronous invoke indent(f_service_) << "var asyncResult = Begin_" << funname << "(null, null"; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { 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_ << "End_" << funname << "(asyncResult);" << 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; if (!async_) { indent(f_service_) << "#else" << endl; indent(f_service_) << "public " << function_signature(&send_function) << endl; indent(f_service_) << "#endif" << endl; } scope_up(f_service_); f_service_ << indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", 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) << " = " << (*fld_iter)->get_name() << ";" << endl; } f_service_ << indent() << "args.Write(oprot_);" << endl << indent() << "oprot_.WriteMessageEnd();" << endl;; if (!async_) { indent(f_service_) << "#if SILVERLIGHT" << endl; } indent(f_service_) << "return oprot_.Transport.BeginFlush(callback, state);" << endl; if (!async_) { indent(f_service_) << "#else" << endl; indent(f_service_) << "oprot_.Transport.Flush();" << endl; indent(f_service_) << "#endif" << 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()); 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_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; } } t_struct *xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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." << (*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; } scope_down(f_service_); f_service_ << endl; } } indent_down(); indent(f_service_) << "}" << endl; } void t_csharp_generator::generate_service_server(t_service* tservice) { vector functions = tservice->get_functions(); vector::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(Iface 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 Iface iface_;" << endl; if (extends.empty()) { f_service_ << indent() << "protected Dictionary processMap_ = new Dictionary();" << 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_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& fields = xs->get_members(); vector::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& xceptions = xs->get_members(); vector::const_iterator x_iter; if (!tfunction->is_oneway()) { f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl; } if (xceptions.size() > 0) { f_service_ << indent() << "try {" << endl; indent_up(); } t_struct* arg_struct = tfunction->get_arglist(); const std::vector& fields = arg_struct->get_members(); vector::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." << prop_name(*f_iter); if (nullable_ && !type_can_be_null((*f_iter)->get_type())) { f_service_ << ".Value"; } } f_service_ << ");" << 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." << prop_name(*x_iter) << " = " << (*x_iter)->get_name() << ";" << endl; indent_down(); f_service_ << indent() << "}"; } else { f_service_ << "}"; } } f_service_ << endl; } if (tfunction->is_oneway()) { f_service_ << indent() << "return;" << endl; scope_down(f_service_); 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.Transport.Flush();" << endl; scope_down(f_service_); f_service_ << endl; } void t_csharp_generator::generate_csharp_union_reader(std::ofstream& out, t_struct* tunion) { // Thanks to THRIFT-1768, we don't need to check for required fields in the union const vector& fields = tunion->get_members(); vector::const_iterator f_iter; indent(out) << "public static " << tunion->get_name() << " Read(TProtocol iprot)" << 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(); indent(out) << "}" << endl << endl; } void t_csharp_generator::generate_deserialize_field(ofstream& 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 (((t_base_type*)type)->is_binary()) { out << "ReadBinary();"; } else { out << "ReadString();"; } break; case t_base_type::TYPE_BOOL: out << "ReadBool();"; break; case t_base_type::TYPE_BYTE: 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 " + 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(ofstream& 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(ofstream& 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) << "();" <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(ofstream& 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(ofstream& out, t_set* tset, string prefix) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); indent(out) << declare_field(&felem, true) << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".Add(" << elem << ");" << endl; } void t_csharp_generator::generate_deserialize_list_element(ofstream& out, t_list* tlist, string prefix) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); indent(out) << declare_field(&felem, true) << endl; generate_deserialize_field(out, &felem); indent(out) << prefix << ".Add(" << elem << ");" << endl; } void t_csharp_generator::generate_serialize_field(ofstream& 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 (((t_base_type*)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_BYTE: 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 " + 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(ofstream& out, t_struct* tstruct, string prefix) { (void) tstruct; out << indent() << prefix << ".Write(oprot);" << endl; } void t_csharp_generator::generate_serialize_container(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset) { generate_csharp_property(out, tfield, isPublic, generateIsset, "_"); } void t_csharp_generator::generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset, std::string fieldPrefix) { if((serialize_||wcf_) && isPublic) { indent(out) << "[DataMember]" << 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; } } 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." << 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." << 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; } std::string t_csharp_generator::prop_name(t_field* tfield) { string name (tfield->get_name()); name[0] = toupper(name[0]); 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 + "." + ttype->get_name() + postfix; } } return 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_BYTE: 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 " + 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)) { 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_BYTE: 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) + " " + 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 " + 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) + " " + 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 + " " + prefix + tfunction->get_name() + "Async(" + argument_list(tfunction->get_arglist()) + ")"; } string t_csharp_generator::argument_list(t_struct* tstruct) { string result = ""; const vector& fields = tstruct->get_members(); vector::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()) + " " + (*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_BYTE: 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(ofstream &out, string contents) { generate_docstring_comment(out, "/// \n", "/// ", contents, "/// \n"); } void t_csharp_generator::generate_csharp_doc(ofstream &out, t_field* field) { if (field->get_type()->is_enum()) { string combined_message = field->get_doc() + "\nget_type()) + "\"/>"; generate_csharp_docstring_comment(out, combined_message); } else { generate_csharp_doc(out, (t_doc*)field); } } void t_csharp_generator::generate_csharp_doc(ofstream &out, t_doc* tdoc) { if (tdoc->has_doc()) { generate_csharp_docstring_comment(out, tdoc->get_doc()); } } void t_csharp_generator::generate_csharp_doc(ofstream &out, t_function* tfunction) { if (tfunction->has_doc()) { stringstream ps; const vector& fields = tfunction->get_arglist()->get_members(); vector::const_iterator p_iter; for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { t_field* p = *p_iter; ps << "\nget_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 << ""; } generate_docstring_comment(out, "", "/// ", "\n" + tfunction->get_doc() + "" + 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" " asyncctp: Adds Async CTP support using TaskEx.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" ) thrift-compiler_0.9.1/cpp/src/generate/t_delphi_generator.cc0000644000175000017500000033407012203157755025025 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "platform.h" #include "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& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("ansistr_binary"); ansistr_binary_ = (iter != parsed_options.end()); iter = parsed_options.find("register_types"); register_types_ = (iter != parsed_options.end()); out_dir_base_ = "gen-delphi"; escape_.clear(); escape_['\''] = "''"; } void init_generator(); void close_generator(); void generate_consts(std::vector consts); void generate_typedef (t_typedef* ttypedef); void generate_enum (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 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_factroy_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_factroy_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); 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 = ""); 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_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 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, 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); virtual std::string 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"; } 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_enum; bool has_const; std::string namespace_dir_; std::map delphi_keywords; std::map delphi_reserved_method; std::map delphi_reserved_method_exception; std::map types_known; std::list typedefs_pending; std::vector uses_list; void create_keywords(); bool find_keyword( std::map& 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_; 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(); }; }; bool t_delphi_generator::find_keyword( std::map& keyword_map, std::string name) { int len = name.length(); if ( len <= 0 ) { return false; } int 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(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; delphi_keywords["result"] = 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["methodaddress"] = 1; delphi_reserved_method["methodname"] = 1; delphi_reserved_method["fieldaddress"] = 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::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_enum = false; has_const = false; create_keywords(); add_delphi_uses_list("Classes"); add_delphi_uses_list("SysUtils"); add_delphi_uses_list("Generics.Collections"); 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& includes = program_->get_includes(); for (size_t i = 0; i < includes.size(); ++i) { unitname = includes[i]->get_name(); nsname = includes[i]->get_namespace("delphi"); if ( "" != nsname) { unitname = 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, "_" ); } } std::string f_name = get_out_dir() + "/" + unitname + ".pas"; std::ofstream f_all; f_all.open( f_name.c_str() ); f_all << autogen_comment() << endl; f_all << "unit " << unitname << ";" << endl << endl; f_all << "interface" << endl << endl; f_all << "uses" << endl; indent_up(); vector::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_down(); f_all << "type" << endl; f_all << s_forward_decr.str(); 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; f_all << "initialization" << endl; if ( has_const ) { f_all << "{$IF CompilerVersion < 21.0}" << endl; f_all << " TConstants_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}" << endl; f_all << " TConstants_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::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_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(); indent(s_struct) << type_name(ttypedef) << " = "; bool container = type->is_list() || type->is_map() || type->is_set(); // commented out: the benefit is not big enough to risk breaking existing code //if( ! container) // s_struct << "type "; //the "type A = type B" syntax leads to E2574 with generics s_struct << type_name(ttypedef->get_type(), ! container) << ";" << 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::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_byte)] = 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(); indent(s_enum) << type_name(tenum,true,true) << " = " << "(" << endl; indent_up(); vector constants = tenum->get_constants(); vector::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; } indent(s_enum) << normalize_name((*c_iter)->get_name()) << " = " << value; } s_enum << endl; indent_down(); indent(s_enum) << ");" << endl << endl; indent_down(); } void t_delphi_generator::generate_consts(std::vector consts) { if (consts.empty()){ return; } has_const = true; indent_up(); indent(s_const) << "TConstants = class" << endl; indent(s_const) << "private" << endl; indent_up(); vector::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { 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) { 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 TConstants.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 TConstants.Destroy;" << 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()); } 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) { initialize_field( vars, code, "TConstants.F" + prop_name( (*c_iter)->get_name()), (*c_iter)->get_type(), (*c_iter)->get_value()); } indent_down_impl(); indent_impl(s_const_impl) << "procedure TConstants_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 TConstants_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(), "TConstants" ); } 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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; } void t_delphi_generator::print_const_prop(std::ostream& out, string name, t_type* type, t_const_value* value) { (void) value; indent(out) << "class property " << name << ": " << type_name(type) << " read F" << name << ";" << 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()) { 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( type, true, false, type->is_xception(), type->is_xception()); indent_impl(out) << name << " := " << typname << ".Create;" << endl; print_const_def_value( vars, out, name, type, 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_BYTE: 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(); } else { render << value->get_double(); } break; default: render << ""; } } 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& members = tstruct->get_members(); vector::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; indent_impl(out) << "F" << exception_factory_name << " := T" << exception_factory_name << "Impl.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 ( 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) { 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) { 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& members = tstruct->get_members(); vector::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) { 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 << ": Boolean read Get" << isset_name << ";" << endl; } } } indent_down(); indent(out) << "end;" << endl << endl; } indent(out) << struct_name << " = "; if (is_final) { out << "sealed "; } out << "class("; if ( is_exception && (! is_x_factory)) { out << "TException"; } else { out << "TInterfacedObject, IBase, " << 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 << ": 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 << ": 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) << "property " << exception_factory_name << ": " << struct_intf_name << " read F" << exception_factory_name << " write F" << exception_factory_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 << ": Boolean read Get" << isset_name << ";" << endl; } } } indent_down(); indent(out) << "end;" << endl << endl; } void t_delphi_generator::generate_service(t_service* tservice) { indent_up(); 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) { string extends = ""; string extends_iface = ""; indent_up(); if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends(), true, true); extends_iface = extends + ".Iface"; indent(s_service) << "Iface = interface(" << extends_iface << ")" << endl; } else { indent(s_service) << "Iface = interface" << endl; } indent_up(); vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { indent(s_service) << function_signature(*f_iter) << endl; } indent_down(); indent(s_service) << "end;" << endl << endl; indent_down(); } void t_delphi_generator::generate_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::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 = ""; if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_client = extends + ".Client, "; } if (tservice->get_extends() != NULL) { extends = type_name(tservice->get_extends(), true, true); extends_client = extends + ".TClient"; indent(s_service) << "TClient = class(" << extends_client << ", Iface)" << endl; } else { indent(s_service) << "TClient = class( TInterfacedObject, Iface)" << 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) << "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_: 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 functions = tservice->get_functions(); vector::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(); indent(s_service) << function_signature(*f_iter) << 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(); indent_impl(s_service_impl) << function_signature(*f_iter, full_cls) << endl; indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "send_" << funname << "("; t_struct* arg_struct = (*f_iter)->get_arglist(); const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; 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; } 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"); indent(s_service) << function_signature(&send_function) << endl; indent_impl(s_service_impl) << function_signature(&send_function, full_cls) << endl; indent_impl(s_service_impl) << "var" << endl; indent_up_impl(); indent_impl(s_service_impl) << "args : " << args_intfnm << ";" << endl; indent_impl(s_service_impl) << "msg : IMessage;" << 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) << "msg := TMessageImpl.Create('" << funname << "', TMessageType.Call, seqid_);" << endl; indent_impl(s_service_impl) << "oprot_.WriteMessageBegin( msg );" << endl; indent_impl(s_service_impl) << "args := " << args_clsnm << "Impl.Create();" << endl; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { indent_impl(s_service_impl) << "args." << prop_name(*fld_iter) << " := " << normalize_name( (*fld_iter)->get_name()) << ";" << endl; } indent_impl(s_service_impl) << "args.Write(oprot_);" << endl; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { indent_impl(s_service_impl) << "args." << 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& xceptions = xs->get_members(); indent(s_service) << function_signature(&recv_function) << endl; indent_impl(s_service_impl) << function_signature(&recv_function, full_cls) << endl; indent_impl(s_service_impl) << "var" << endl; indent_up_impl(); indent_impl(s_service_impl) << "msg : IMessage;" << endl; if ( xceptions.size() > 0) { indent_impl(s_service_impl) << "ex : Exception;" << endl; } indent_impl(s_service_impl) << "x : TApplicationException;" << endl; indent_impl(s_service_impl) << "ret : " << result_intfnm << ";" << endl; indent_down_impl(); indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "msg := iprot_.ReadMessageBegin();" << endl; indent_impl(s_service_impl) << "if (msg.Type_ = TMessageType.Exception) then" << endl; indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "x := TApplicationException.Read(iprot_);" << endl; indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl; indent_impl(s_service_impl) << "raise x;" << endl; indent_down_impl(); indent_impl(s_service_impl) << "end;" << endl; indent_impl(s_service_impl) << "ret := " << result_clsnm << "Impl.Create();" << endl; indent_impl(s_service_impl) << "ret.Read(iprot_);" << endl; indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl; if (!(*f_iter)->get_returntype()->is_void()) { indent_impl(s_service_impl) << "if (ret.__isset_success) then" << endl; indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "Result := ret.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) << "ret.Success := nil;" << endl; } indent_impl(s_service_impl) << "Exit;" << endl; indent_down_impl(); indent_impl(s_service_impl) << "end;" << endl; } vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { indent_impl(s_service_impl) << "if (ret.__isset_" << prop_name(*x_iter) << ") then" << endl; indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); indent_impl(s_service_impl) << "ex := ret." << prop_name(*x_iter) << ".CreateException;" << endl; indent_impl(s_service_impl) << "raise ex;" << endl; indent_down_impl(); indent_impl(s_service_impl) << "end;" << endl; } if (!(*f_iter)->get_returntype()->is_void()) { indent_impl(s_service_impl) << "raise TApplicationException.Create(TApplicationException.TExceptionType.MissingResult, '" << (*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 functions = tservice->get_functions(); vector::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.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: Integer; const iprot: IProtocol; const oprot: IProtocol);" << endl; indent_down(); indent_down(); indent(s_service) << "protected" << endl; indent_up(); indent(s_service) << "processMap_: IThriftDictionary;" << endl; indent_down(); } indent(s_service) << "public" << endl; indent_up(); if (extends.empty()) { indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol): Boolean;" << endl; } else { indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol): Boolean; reintroduce;" << endl; } indent_impl(s_service_impl) << "function " << full_cls << ".Process( const iprot: IProtocol; const oprot: IProtocol): Boolean;" << endl;; indent_impl(s_service_impl) << "var" << endl; indent_up_impl(); indent_impl(s_service_impl) << "msg : IMessage;" << endl; indent_impl(s_service_impl) << "fn : TProcessFunction;" << endl; indent_impl(s_service_impl) << "x : TApplicationException;" << 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 := TApplicationException.Create(TApplicationException.TExceptionType.UnknownMethod, 'Invalid method name: ''' + msg.Name + '''');" << endl; indent_impl(s_service_impl) << "msg := TMessageImpl.Create(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; 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) << "Result := False;" << endl; indent_impl(s_service_impl) << "Exit;" << 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& fields = xs->get_members(); vector::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: Integer; const iprot: IProtocol; const oprot: IProtocol);" << 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: Integer; const iprot: IProtocol; const oprot: IProtocol);" << 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: IMessage;" << endl; indent_impl(s_service_impl) << "ret: " << result_intfnm << ";" << endl; } indent_down_impl(); indent_impl(s_service_impl) << "begin" << endl; indent_up_impl(); 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; t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; if (!tfunction->is_oneway()) { indent_impl(s_service_impl) << "ret := " << result_clsnm << "Impl.Create;" << endl; } if (!tfunction->is_oneway() && xceptions.size() > 0) { indent_impl(s_service_impl) << "try" << endl; indent_up_impl(); } t_struct* arg_struct = tfunction->get_arglist(); const std::vector& fields = arg_struct->get_members(); vector::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; } if (!tfunction->is_oneway() && xceptions.size() > 0) { 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" << endl; indent_impl(s_service_impl) << "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_down_impl(); indent_impl(s_service_impl) << "end;" << endl; } if (! tfunction->is_oneway()) { indent_impl(s_service_impl) << "msg := TMessageImpl.Create('" << 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; } 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 (((t_base_type*)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_BYTE: 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 " + 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 + ": IMap;"; } else if (ttype->is_set()) { local_var = obj + ": ISet;"; } else if (ttype->is_list()) { local_var = obj + ": IList;"; } local_vars << " " << local_var << endl; counter = tmp("_i"); local_var = counter + ": 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 (((t_base_type*)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_BYTE: 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 " + tbase; } } else if (type->is_enum()) { out << "WriteI32(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 << " : IMap;" << endl; indent_impl(out) << obj << " := TMapImpl.Create( " << 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 << " : ISet;" << endl; indent_impl(out) << obj << " := TSetImpl.Create(" << 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 << " : IList;" << endl; indent_impl(out) << obj << " := TListImpl.Create(" << 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(); 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()) { return normalize_name( "T"+((t_typedef*)ttype)->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_BYTE: 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*)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 "AnsiString"; } else { return "TBytes"; } } else { return "string"; } case t_base_type::TYPE_BOOL: return "Boolean"; case t_base_type::TYPE_BYTE: return "ShortInt"; case t_base_type::TYPE_I16: return "SmallInt"; case t_base_type::TYPE_I32: return "Integer"; case t_base_type::TYPE_I64: return "Int64"; case t_base_type::TYPE_DOUBLE: return "Double"; default: throw "compiler error: no Delphi name for base type " + 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, std::string full_cls, bool is_xception) { t_type* ttype = tfunction->get_returntype(); string prefix; if (full_cls == "") { prefix = ""; } else { prefix = full_cls + "."; } 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& fields = tstruct->get_members(); vector::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& fields = tstruct->get_members(); vector::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_BYTE: 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 (((t_base_type*)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_BYTE: 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) << ": 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_factroy_name) { (void) type; 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_factroy_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) << "F" << xception_factroy_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 << ": 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." << factory_name << " := Self;" << endl; const vector& fields = tstruct->get_members(); vector::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" << endl; indent_impl(out) << "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& fields = tstruct->get_members(); vector::const_iterator f_iter; indent_impl(code_block) << "begin" << endl; indent_up_impl(); // 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) << " : 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" << endl; indent_impl(code_block) << "begin" << endl; indent_up_impl(); generate_deserialize_field(code_block, is_exception, *f_iter, "", 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" << endl; indent_impl(code_block) << "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 TProtocolException.Create( TProtocolException.INVALID_DATA, '" << prop_name(*f_iter, is_exception) << "');" << 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_ : IField;" << endl; indent_impl(out) << "struc : IStruct;" << 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& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; indent_impl(code_block) << "begin" << endl; indent_up_impl(); indent_impl(code_block) << "struc := TStructImpl.Create('" << name << "');" << endl; indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl; if (fields.size() > 0) { indent_impl(code_block) << "field_ := TFieldImpl.Create;" << endl; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (! first) { indent_impl(code_block) << "end else" << endl; } 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, "", local_vars); indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl; indent_down_impl(); } if (! first) { 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 : IStruct;" << endl; if (fields.size() > 0) { indent_impl(out) << "field_ : IField;" << 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& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; indent_impl(code_block) << "begin" << endl; indent_up_impl(); indent_impl(code_block) << "struc := TStructImpl.Create('" << name << "');" << endl; indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl; if (fields.size() > 0) { indent_impl(code_block) << "field_ := TFieldImpl.Create;" << endl; } 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(code_block) << "if (" << prop_name((*f_iter), is_exception) << " <> nil)"; if (is_optional) { code_block << " and __isset_" << prop_name(*f_iter,is_exception); } code_block << " then" << endl; indent_impl(code_block) << "begin" << endl; indent_up_impl(); } else { if (is_optional) { indent_impl(code_block) << "if (__isset_" << prop_name(*f_iter,is_exception) << ") then" << endl; indent_impl(code_block) << "begin" << endl; indent_up_impl(); } else { indent_impl(code_block) << "// required field" << endl; } } 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, "", local_vars); indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl; if (null_allowed || is_optional) { 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 : IStruct;" << endl; if (fields.size() > 0) { indent_impl(out) << "field_ : IField;" << 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& fields = tstruct->get_members(); vector::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 = "sb"; 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; 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(); bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; 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(); if (ttype->is_xception() || ttype->is_struct()) { indent_impl(out) << "if (" << prop_name((*f_iter), is_exception) << " = nil) then " << tmp_sb << ".Append('') else " << tmp_sb << ".Append("<< prop_name((*f_iter), is_exception) << ".ToString());" << endl; } else if (ttype->is_enum()) { indent_impl(out) << tmp_sb << ".Append(Integer(" << prop_name((*f_iter), is_exception) << "));" << endl; } else { indent_impl(out) << tmp_sb << ".Append(" << prop_name((*f_iter), is_exception) << ");" << endl; } } indent_impl(out) << tmp_sb << ".Append(')');" << endl; indent_impl(out) << "Result := " << tmp_sb << ".ToString;" << 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"); thrift-compiler_0.9.1/cpp/src/generate/t_perl_generator.cc0000644000175000017500000015470512203157755024527 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "t_oop_generator.h" #include "platform.h" #include "version.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 /** * PERL code generator. * */ class t_perl_generator : public t_oop_generator { public: t_perl_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-perl"; escape_['$'] = "\\$"; escape_['@'] = "\\@"; } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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::ofstream& out, t_struct* tstruct, bool is_xception=false); void generate_perl_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_perl_struct_writer(std::ofstream& 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); /** * Serialization constructs */ void generate_deserialize_field (std::ofstream &out, t_field* tfield, std::string prefix="", bool inclass=false); void generate_deserialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream &out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream &out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream &out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream &out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream &out, t_map* tmap, std::string kiter, std::string viter); void generate_serialize_set_element (std::ofstream &out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream &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() { 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& 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 dirs; perl_namespace_dirs(program_, dirs); std::list::iterator it; for (it = dirs.begin(); it != dirs.end(); it++) { outdir += *it + "/"; } return outdir; } private: /** * File streams */ std::ofstream f_types_; std::ofstream f_consts_; std::ofstream f_helpers_; std::ofstream f_service_; }; /** * 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 dirs; perl_namespace_dirs(program_, dirs); std::list::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;"<get_name()<<";"< constants = tenum->get_constants(); vector::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_BYTE: 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 << "new " << perl_namespace(type->get_program()) << type->get_name() << "({" << endl; indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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 << render_const_value(g_type_string, v_iter->first); out << " => "; out << render_const_value(field_type, v_iter->second); out << ","; out << endl; } 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; const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { out << render_const_value(ktype, v_iter->first); out << " => "; out << render_const_value(vtype, v_iter->second); out << "," << endl; } 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; const vector& val = value->get_list(); vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { out << render_const_value(etype, *v_iter); if (type->is_set()) { out << " => 1"; } out << "," << endl; } 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_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(ofstream& out, t_struct* tstruct, bool is_exception) { const vector& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; out << "sub read {" <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 == 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(ofstream& out, t_struct* tstruct) { string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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 <get_program()) << "Types;" << endl; 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 functions = tservice->get_functions(); vector::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);"<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(TType::STRUCT);" << endl << indent() << "$input->readMessageEnd();" << endl << indent() << "my $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_ << indent() << "}" << endl << indent() << "$self->$methodname($rseqid, $input, $output);" << endl << indent() << "return 1;" << endl; indent_down(); f_service_ << "}" << endl <get_name() << " {"<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 = new " << argsname << "();" << endl << indent() << "$args->read($input);" << endl; f_service_ << indent() << "$input->readMessageEnd();" << endl; t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; // Declare result for non oneway function if (!tfunction->is_oneway()) { f_service_ << indent() << "my $result = new " << resultname << "();" << 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& fields = arg_struct->get_members(); vector::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; if (!tfunction->is_oneway()) { indent_up(); f_service_ << indent() << "$result->{" << (*x_iter)->get_name() << "} = $@;" << endl; indent_down(); f_service_ << indent(); } } f_service_ << "}" << endl; } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { f_service_ << indent() << "return;" << endl; indent_down(); f_service_ << "}" << endl; return; } // Serialize the request header f_service_ << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', 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 functions = tservice->get_functions(); vector::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& fields = xs->get_members(); vector::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 functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << "sub " << function_signature(*f_iter) <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 functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << "sub " << (*f_iter)->get_name() << "{" <& args = (*f_iter)->get_arglist()->get_members(); vector::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()<{impl}->" << (*f_iter)->get_name() << "(" << argument_list((*f_iter)->get_arglist()) << ");" << endl; indent_down(); indent(f_service_) << "}" << endl <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 {"<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);"< functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::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() << "', TMessageType::CALL, $self->{seqid});" << endl; f_service_ << indent() << "my $args = 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; } // 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 == TMessageType::EXCEPTION) {" << endl << indent() << " my $x = new TApplicationException();" << endl << indent() << " $x->read($self->{input});" << endl << indent() << " $self->{input}->readMessageEnd();" << endl << indent() << " die $x;" << endl << indent() << "}" << endl; f_service_ << indent() << "my $result = new " << resultname << "();" << 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& xceptions = xs->get_members(); vector::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_ << "}"<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_BYTE: 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(ofstream &out, t_struct* tstruct, string prefix) { out << indent() << "$" << prefix << " = new " << perl_namespace(tstruct->get_program()) << tstruct->get_name() << "();" << endl << indent() << "$xfer += $" << prefix << "->read($input);" << endl; } void t_perl_generator::generate_deserialize_container(ofstream &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_byte, ktype); t_field fvtype(g_type_byte, vtype); t_field fetype(g_type_byte, 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(ofstream &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(ofstream &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(ofstream &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(ofstream &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_BYTE: 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(ofstream &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(ofstream &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 ($"<is_set()) { string iter = tmp("iter"); indent(out) << "foreach my $"<is_list()) { string iter = tmp("iter"); indent(out) << "foreach my $"<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(ofstream &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(ofstream &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(ofstream &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_BYTE: 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 += " = new " + perl_namespace(type->get_program()) + type->get_name() + "()"; } 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 &fields = tfunction->get_arglist()->get_members(); vector::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& fields = tstruct->get_members(); vector::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 "TType::STRING"; case t_base_type::TYPE_BOOL: return "TType::BOOL"; case t_base_type::TYPE_BYTE: 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(perl, "Perl", "") thrift-compiler_0.9.1/cpp/src/generate/t_html_generator.cc0000644000175000017500000007065212203157755024527 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "t_generator.h" #include "t_html_generator.h" #include "platform.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& parsed_options, const std::string& option_string) : t_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-html"; input_type_ = INPUT_UNKNOWN; std::map::const_iterator iter; iter = parsed_options.find("standalone"); standalone_ = (iter != parsed_options.end()); escape_.clear(); escape_['&'] = "&"; escape_['<'] = "<"; escape_['>'] = ">"; escape_['"'] = """; escape_['\''] = "'"; } void generate_program(); void generate_program_toc(); void generate_program_toc_row(t_program* tprog); void generate_program_toc_rows(t_program* tprog, std::vector& finished); void generate_index(); std::string escape_html(std::string const & str); void generate_css(); void generate_css_content(std::ofstream & 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); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_service (t_service* tservice); void generate_xception(t_struct* txception); void print_doc (t_doc* tdoc); int print_type (t_type* ttype); void print_const_value(t_const_value* tvalue); void print_fn_args_doc(t_function* tfunction); private: std::ofstream f_out_; std::string current_file_; input_type input_type_; bool standalone_; }; /** * Emits the Table of Contents links at the top of the module's page */ void t_html_generator::generate_program_toc() { f_out_ << "" << "" << endl; generate_program_toc_row(program_); f_out_ << "
ModuleServicesData typesConstants
" << 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& finished) { for (vector::iterator iter = finished.begin(); iter != finished.end(); iter++) { if (tprog->get_path() == (*iter)->get_path()) { return; } } finished.push_back(tprog); generate_program_toc_row(tprog); vector includes = tprog->get_includes(); for (vector::iterator iter = includes.begin(); iter != includes.end(); iter++) { generate_program_toc_rows(*iter, 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_ << "" << endl << "" << tprog->get_name() << ""; if (!tprog->get_services().empty()) { vector services = tprog->get_services(); vector::iterator sv_iter; for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { string name = get_service_name(*sv_iter); f_out_ << "" << name << "
" << endl; f_out_ << "
    " << endl; map fn_html; vector functions = (*sv_iter)->get_functions(); vector::iterator fn_iter; for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { string fn_name = (*fn_iter)->get_name(); string html = "
  • " + fn_name + "
  • "; fn_html.insert(pair(fn_name, html)); } for (map::iterator html_iter = fn_html.begin(); html_iter != fn_html.end(); html_iter++) { f_out_ << html_iter->second << endl; } f_out_ << "
" << endl; } } f_out_ << "" << endl << ""; map data_types; if (!tprog->get_enums().empty()) { vector enums = tprog->get_enums(); vector::iterator en_iter; for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { string name = (*en_iter)->get_name(); // f_out_ << "" << name // << "
" << endl; string html = "" + name + ""; data_types.insert(pair(name, html)); } } if (!tprog->get_typedefs().empty()) { vector typedefs = tprog->get_typedefs(); vector::iterator td_iter; for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { string name = (*td_iter)->get_symbolic(); // f_out_ << "" << name // << "
" << endl; string html = "" + name + ""; data_types.insert(pair(name, html)); } } if (!tprog->get_objects().empty()) { vector objects = tprog->get_objects(); vector::iterator o_iter; for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { string name = (*o_iter)->get_name(); //f_out_ << "" << name //<< "
" << endl; string html = "" + name + ""; data_types.insert(pair(name, html)); } } for (map::iterator dt_iter = data_types.begin(); dt_iter != data_types.end(); dt_iter++) { f_out_ << dt_iter->second << "
" << endl; } f_out_ << "" << endl << ""; if (!tprog->get_consts().empty()) { map const_html; vector consts = tprog->get_consts(); vector::iterator con_iter; for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) { string name = (*con_iter)->get_name(); string html ="" + name + ""; const_html.insert(pair(name, html)); } for (map::iterator con_iter = const_html.begin(); con_iter != const_html.end(); con_iter++) { f_out_ << con_iter->second << "
" << endl; } } f_out_ << "" << endl << ""; } /** * 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_ << "" << endl; f_out_ << "" << endl; f_out_ << "" << endl; f_out_ << "" << endl; generate_style_tag(); f_out_ << "Thrift module: " << program_->get_name() << "" << endl << "
" << endl << "

Thrift module: " << program_->get_name() << "

" << endl; print_doc(program_); generate_program_toc(); if (!program_->get_consts().empty()) { f_out_ << "

Constants

" << endl; vector consts = program_->get_consts(); f_out_ << ""; f_out_ << "" << endl; generate_consts(consts); f_out_ << "
ConstantTypeValue
"; } if (!program_->get_enums().empty()) { f_out_ << "

Enumerations

" << endl; // Generate enums vector enums = program_->get_enums(); vector::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_ << "

Type declarations

" << endl; // Generate typedefs vector typedefs = program_->get_typedefs(); vector::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_ << "

Data structures

" << endl; // Generate structs and exceptions in declared order vector objects = program_->get_objects(); vector::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_ << "

Services

" << endl; // Generate services vector services = program_->get_services(); vector::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_ << "
" << 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_ << "" << endl; generate_style_tag(); f_out_ << "All Thrift declarations" << endl << "
" << endl << "

All Thrift declarations

" << endl; f_out_ << "" << "" << endl; vector programs; generate_program_toc_rows(program_, programs); f_out_ << "
ModuleServicesData typesConstants
" << endl; f_out_ << "
" << 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::ofstream & 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_ << "" << endl; } else { f_out_ << "" << endl; } } /** * Returns the target file for a 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()) { string doc = tdoc->get_doc(); size_t index; while ((index = doc.find_first_of("\r\n")) != string::npos) { if (index == 0) { f_out_ << "

" << endl; } else { f_out_ << escape_html( doc.substr(0, index)) << endl; } if (index + 1 < doc.size() && doc.at(index) != doc.at(index + 1) && (doc.at(index + 1) == '\r' || doc.at(index + 1) == '\n')) { index++; } doc = doc.substr(index + 1); } f_out_ << escape_html(doc) << "
"; } } 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; } 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 str; } // convert unsafe chars to their &#; 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; } // some control code? if( (0 <= ic) && (31 >= ic)) { switch( c) { case '\r' : case '\n' : result << "
"; break; case '\t' : result << " "; 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 result.str(); } /** * Prints out the provided type in HTML */ int t_html_generator::print_type(t_type* ttype) { int len = 0; f_out_ << ""; if (ttype->is_container()) { if (ttype->is_list()) { f_out_ << "list<"; len = 6 + print_type(((t_list*)ttype)->get_elem_type()); f_out_ << ">"; } else if (ttype->is_set()) { f_out_ << "set<"; len = 5 + print_type(((t_set*)ttype)->get_elem_type()); f_out_ << ">"; } else if (ttype->is_map()) { f_out_ << "map<"; len = 5 + print_type(((t_map*)ttype)->get_key_type()); f_out_ << ", "; len += print_type(((t_map*)ttype)->get_val_type()); f_out_ << ">"; } } else if (ttype->is_base_type()) { f_out_ << (((t_base_type*)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_ << "
is_typedef()) { f_out_ << "Typedef_"; } 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 << ""; } f_out_ << ""; return len; } /** * Prints out an HTML representation of the provided constant value */ void t_html_generator::print_const_value(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_ << '"' << escape_html( get_escaped_string(tvalue)) << '"'; break; case t_const_value::CV_MAP: { f_out_ << "{ "; map map_elems = tvalue->get_map(); map::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(map_iter->first); f_out_ << " = "; print_const_value(map_iter->second); } f_out_ << " }"; } break; case t_const_value::CV_LIST: { f_out_ << "{ "; vector list_elems = tvalue->get_list();; vector::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(*list_iter); } f_out_ << " }"; } break; default: f_out_ << "UNKNOWN"; break; } } /** * 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 args = tfunction->get_arglist()->get_members(); vector::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_ << "

get_name() << "\">Parameters

" << endl; f_out_ << ""; f_out_ << ""; for ( ; arg_iter != args.end(); arg_iter++) { f_out_ << "" << endl; } f_out_ << "
NameDescription
" << (*arg_iter)->get_name(); f_out_ << ""; f_out_ << escape_html( (*arg_iter)->get_doc()); f_out_ << "
"; } } has_docs = false; vector excepts = tfunction->get_xceptions()->get_members(); vector::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_ << "

get_name() << "\">Exceptions

" << endl; f_out_ << ""; f_out_ << ""; for ( ; ex_iter != excepts.end(); ex_iter++) { f_out_ << "" << endl; } f_out_ << "
TypeDescription
" << (*ex_iter)->get_type()->get_name(); f_out_ << ""; f_out_ << escape_html( (*ex_iter)->get_doc()); f_out_ << "
"; } } } /** * Generates a typedef. * * @param ttypedef The type definition */ void t_html_generator::generate_typedef(t_typedef* ttypedef) { string name = ttypedef->get_name(); f_out_ << "
"; f_out_ << "

Typedef: " << name << "

" << endl; f_out_ << "

Base type: "; print_type(ttypedef->get_type()); f_out_ << "

" << endl; print_doc(ttypedef); f_out_ << "
" << 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_ << "
"; f_out_ << "

Enumeration: " << name << "

" << endl; print_doc(tenum); vector values = tenum->get_constants(); vector::iterator val_iter; f_out_ << "
" << endl; for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { f_out_ << "" << endl; } f_out_ << "
"; f_out_ << (*val_iter)->get_name(); f_out_ << ""; f_out_ << (*val_iter)->get_value(); f_out_ << "" << endl; print_doc((*val_iter)); f_out_ << "
" << endl; } /** * Generates a constant value */ void t_html_generator::generate_const(t_const* tconst) { string name = tconst->get_name(); f_out_ << "" << name << ""; print_type(tconst->get_type()); f_out_ << ""; print_const_value(tconst->get_value()); f_out_ << ""; if (tconst->has_doc()) { f_out_ << "
"; print_doc(tconst); f_out_ << "
"; } } /** * 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_ << "
"; f_out_ << "

"; if (tstruct->is_xception()) { f_out_ << "Exception: "; } else if (tstruct->is_union()) { f_out_ << "Union: "; } else { f_out_ << "Struct: "; } f_out_ << name << "

" << endl; vector members = tstruct->get_members(); vector::iterator mem_iter = members.begin(); f_out_ << ""; f_out_ << "" << endl; for ( ; mem_iter != members.end(); mem_iter++) { f_out_ << "" << endl; } f_out_ << "
KeyFieldTypeDescriptionRequirednessDefault value
" << (*mem_iter)->get_key() << ""; f_out_ << (*mem_iter)->get_name(); f_out_ << ""; print_type((*mem_iter)->get_type()); f_out_ << ""; f_out_ << escape_html( (*mem_iter)->get_doc()); f_out_ << ""; 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_ << ""; t_const_value* default_val = (*mem_iter)->get_value(); if (default_val != NULL) { print_const_value(default_val); } f_out_ << "

"; print_doc(tstruct); f_out_ << "
"; } /** * 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_ << "

Service: " << service_name_ << "

" << endl; if (tservice->get_extends()) { f_out_ << "
extends "; print_type(tservice->get_extends()); f_out_ << "
\n"; } print_doc(tservice); vector functions = tservice->get_functions(); vector::iterator fn_iter = functions.begin(); for ( ; fn_iter != functions.end(); fn_iter++) { string fn_name = (*fn_iter)->get_name(); f_out_ << "
"; f_out_ << "

Function: " << service_name_ << "." << fn_name << "

" << endl; f_out_ << "
";
    int offset = print_type((*fn_iter)->get_returntype());
    bool first = true;
    f_out_ << " " << fn_name << "(";
    offset += fn_name.size() + 2;
    vector args = (*fn_iter)->get_arglist()->get_members();
    vector::iterator arg_iter = args.begin();
    for ( ; arg_iter != args.end(); arg_iter++) {
      if (!first) {
        f_out_ << "," << endl;
        for (int 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_value());
      }
    }
    f_out_ << ")" << endl;
    first = true;
    vector excepts = (*fn_iter)->get_xceptions()->get_members();
    vector::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_ << "
"; print_doc(*fn_iter); print_fn_args_doc(*fn_iter); f_out_ << "
"; } } 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" ) thrift-compiler_0.9.1/cpp/src/generate/t_oop_generator.h0000644000175000017500000000423412203157755024213 0ustar eevanseevans00000000000000/* * 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 #include #include "globals.h" #include "t_generator.h" #include "version.h" #include /** * 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; } /** * 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") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + " * @generated\n" + " */\n"; } }; #endif thrift-compiler_0.9.1/cpp/src/generate/t_php_generator.cc0000644000175000017500000023035112203157755024344 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include "t_oop_generator.h" #include "platform.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 #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& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("inlined"); binary_inline_ = (iter != parsed_options.end()); iter = parsed_options.find("rest"); rest_ = (iter != parsed_options.end()); iter = parsed_options.find("server"); phps_ = (iter != parsed_options.end()); iter = parsed_options.find("oop"); oop_ = (iter != parsed_options.end()); iter = parsed_options.find("nsglobal"); if(iter != parsed_options.end()) { nsglobal_ = iter->second; } else { nsglobal_ = ""; // by default global namespace is empty } if (oop_ && binary_inline_) { throw "oop and inlined are mutually exclusive."; } out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php"); escape_['$'] = "\\$"; } static bool is_valid_namespace(const std::string& sub_namespace); /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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::ofstream& out, t_struct* tstruct, bool is_xception=false); void generate_php_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_php_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_php_function_helpers(t_function* tfunction); void generate_php_type_spec(std::ofstream &out, t_type* t); void generate_php_struct_spec(std::ofstream &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 (t_service* tservice, t_function* tfunction); /** * Serialization constructs */ void generate_deserialize_field (std::ofstream &out, t_field* tfield, std::string prefix="", bool inclass=false); void generate_deserialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream &out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream &out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream &out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream &out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream &out, t_map* tmap, std::string kiter, std::string viter); void generate_serialize_set_element (std::ofstream &out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream &out, t_list* tlist, std::string iter); /** * 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 addStructSignature = true); std::string type_to_cast(t_type* ttype); std::string type_to_enum(t_type* ttype); 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 + "\\") : ""); } //setting the namespace of a file: my\namespace string php_namespace_suffix(const t_program* p) { string ns = php_namespace_base(p); return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + ns; } //add a directory to allready existing namespace string php_namespace_directory(string directory, bool end = true) { 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 (std::string::iterator it = ns.begin(); it != ns.end(); ++it) { if (*it == '.') { *it = '/'; } } return ns + '/'; } /** * Transform class_method into ClassMethod * * @param str * @return stirng */ string classify(string str) { string classe = ""; vector x = split(str, '_'); for (size_t i = 0; i < x.size(); ++i) { classe = classe + capitalize(x[i]); } return classe; } /** * Split method * @param s * @param delim * @param elems * @return */ vector &split(const string &s, char delim, vector &elems) { stringstream ss(s); string item; while(getline(ss, item, delim)) { elems.push_back(item); } return elems; } vector split(const string &s, char delim) { vector 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 */ std::ofstream f_types_; std::ofstream f_helpers_; std::ofstream 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_; /** * 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 NSx = split(php_namespace_suffix(get_program()), '\\'); package_dir_ = get_out_dir(); for (size_t i = 0; i < NSx.size(); ++i) { package_dir_ = package_dir_ + "/" + NSx[i] + "/"; MKDIR(package_dir_.c_str()); } // Make output file string f_types_name = package_dir_+"Types.php"; f_types_.open(f_types_name.c_str()); // Print header f_types_ << " constants = tenum->get_constants(); vector::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. f_types_ << "final class " << tenum->get_name() << " {" << endl; indent_up(); for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); indent(f_types_) << "const " << (*c_iter)->get_name() << " = " << value << ";" << endl; } indent(f_types_) << "static public $__names = array(" << endl; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); indent(f_types_) << " " << value << " => '" << (*c_iter)->get_name() << "'," << endl; } indent(f_types_) << ");" << endl; indent_down(); f_types_ << "}" << endl << endl; } /** * Generate a constant value */ void t_php_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_ << "$GLOBALS['" << program_name_ << "_CONSTANTS']['" << name << "'] = "; f_types_ << render_const_value(type, value); f_types_ << ";" << 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_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_BYTE: 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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) { generate_php_struct_definition(f_types_, tstruct, is_exception); } void t_php_generator::generate_php_type_spec(ofstream& 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(ofstream& out, t_struct* tstruct) { indent(out) << "if (!isset(self::$_TSPEC)) {" << endl; indent_up(); indent(out) << "self::$_TSPEC = array(" << endl; indent_up(); const vector& members = tstruct->get_members(); vector::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; generate_php_type_spec(out, t); indent(out) << ")," << endl; indent_down(); } indent_down(); indent(out) << " );" << endl; indent_down(); indent(out) << "}" << 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(ofstream& out, t_struct* tstruct, bool is_exception) { const vector& members = tstruct->get_members(); vector::const_iterator m_iter; out << "class " << php_namespace_declaration(tstruct); if (is_exception) { out << " extends " << "TException"; } else if (oop_) { out << " extends " << "TBase"; } out << " {" << endl; indent_up(); indent(out) << "static $_TSPEC;" << endl << endl; 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()); } 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_up(); generate_php_struct_spec(out, tstruct); 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() << " $this->" << (*m_iter)->get_name() << " = $vals['" << (*m_iter)->get_name() << "'];" << endl << indent() << "}" << endl; } } indent_down(); out << indent() << "}" << endl; } scope_down(out); out << endl; out << indent() << "public function getName() {" << endl << indent() << " return '" << tstruct->get_name() << "';" << endl << indent() << "}" << endl << endl; generate_php_struct_reader(out, tstruct); generate_php_struct_writer(out, tstruct); indent_down(); out << indent() << "}" << endl << endl; } /** * Generates the read() method for a struct */ void t_php_generator::generate_php_struct_reader(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; indent(out) << "public function read($input)" << endl; scope_up(out); if (oop_) { indent(out) << "return $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);" << endl; scope_down(out); 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; scope_up(out); // Read beginning field marker if (binary_inline_) { t_field fftype(g_type_byte, "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; 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(); generate_deserialize_field(out, *f_iter, "this->"); indent_down(); out << indent() << "} else {" << endl; if (binary_inline_) { indent(out) << " $xfer += " << "TProtocol::skipBinary($input, $ftype);" << endl; } else { indent(out) << " $xfer += $input->skip($ftype);" << endl; } out << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } // In the default case we skip the field indent(out) << "default:" << endl; if (binary_inline_) { indent(out) << " $xfer += " << "TProtocol::skipBinary($input, $ftype);" << endl; } else { indent(out) << " $xfer += $input->skip($ftype);" << endl; } indent(out) << " break;" << endl; 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; } indent(out) << "return $xfer;" << endl; indent_down(); out << indent() << "}" << endl << endl; } /** * Generates the write() method for a struct */ void t_php_generator::generate_php_struct_writer(ofstream& out, t_struct* tstruct) { string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; if (binary_inline_) { indent(out) << "public function write(&$output) {" << endl; } else { indent(out) << "public function write($output) {" << endl; } indent_up(); if (oop_) { indent(out) << "return $this->_write('" << tstruct->get_name() << "', self::$_TSPEC, $output);" << endl; scope_down(out); 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 << endl; } /** * Generates a thrift service. * * @param tservice The service definition */ void t_php_generator::generate_service(t_service* tservice) { string f_service_name = package_dir_+service_name_+".php"; f_service_.open(f_service_name.c_str()); f_service_ << "get_program()) << ";" << endl; f_service_ << autogen_comment() << php_includes(); f_service_ << endl; // 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); } // 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) { // Generate the dispatch methods vector functions = tservice->get_functions(); vector::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_ << "class " << service_name_ << "Processor" << extends_processor << " {" << endl; indent_up(); if (extends.empty()) { f_service_ << indent() << "protected $handler_ = null;" << endl; } f_service_ << indent() << "public function __construct($handler) {" << endl; if (extends.empty()) { f_service_ << indent() << " $this->handler_ = $handler;" << endl; } else { f_service_ << indent() << " parent::__construct($handler);" << endl; } f_service_ << indent() << "}" << endl << endl; // Generate the server implementation indent(f_service_) << "public function process($input, $output) {" << endl; indent_up(); f_service_ << 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_byte, "mtype"); t_field fseqid(g_type_i32, "rseqid"); generate_deserialize_field(f_service_, &ffname, "", true); generate_deserialize_field(f_service_, &fmtype, "", true); generate_deserialize_field(f_service_, &fseqid, "", true); } else { f_service_ << indent() << "$input->readMessageBegin($fname, $mtype, $rseqid);" << endl; } // HOT: check for method implementation f_service_ << indent() << "$methodname = 'process_'.$fname;" << endl << indent() << "if (!method_exists($this, $methodname)) {" << endl; if (binary_inline_) { f_service_ << indent() << " throw new \\Exception('Function '.$fname.' not implemented.');" << endl; } else { f_service_ << 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; } f_service_ << indent() << "}" << endl << indent() << "$this->$methodname($rseqid, $input, $output);" << endl << indent() << "return true;" << endl; indent_down(); f_service_ << indent() << "}" << 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(); f_service_ << "}" << endl; } /** * Generates a process function definition. * * @param tfunction The function to write a dispatcher for */ void t_php_generator::generate_process_function(t_service* tservice, t_function* tfunction) { // Open function indent(f_service_) << "protected function process_" << tfunction->get_name() << "($seqid, $input, $output) {" << 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"; f_service_ << indent() << "$args = new " << argsname << "();" << endl << indent() << "$args->read($input);" << endl; if (!binary_inline_) { f_service_ << indent() << "$input->readMessageEnd();" << endl; } t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; // Declare result for non oneway function if (!tfunction->is_oneway()) { f_service_ << indent() << "$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& fields = arg_struct->get_members(); vector::const_iterator f_iter; f_service_ << indent(); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { f_service_ << "$result->success = "; } 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; if (!tfunction->is_oneway() && xceptions.size() > 0) { indent_down(); for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << 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(); f_service_ << indent() << "$result->" << (*x_iter)->get_name() << " = $" << (*x_iter)->get_name() << ";" << endl; indent_down(); f_service_ << indent(); } } f_service_ << "}" << endl; } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { f_service_ << indent() << "return;" << endl; indent_down(); f_service_ << indent() << "}" << endl; return; } f_service_ << indent() << "$bin_accel = ($output instanceof " << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');" << endl; f_service_ << indent() << "if ($bin_accel)" << endl; scope_up(f_service_); f_service_ << indent() << "thrift_protocol_write_binary($output, '" << tfunction->get_name() << "', " << "TMessageType::REPLY, $result, $seqid, $output->isStrictWrite());" << endl; scope_down(f_service_); f_service_ << indent() << "else" << endl; scope_up(f_service_); // Serialize the request header if (binary_inline_) { f_service_ << 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 { f_service_ << 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(f_service_); // Close function indent_down(); f_service_ << 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 functions = tservice->get_functions(); vector::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_php_struct_definition(f_service_, ts, false); generate_php_function_helpers(*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_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& fields = xs->get_members(); vector::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } generate_php_struct_definition(f_service_, &result, false); } } /** * 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) { 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"; } f_service_ << "interface " << php_namespace_declaration(tservice) << "If" << extends_if << " {" << endl; indent_up(); vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { indent(f_service_) << "public function " << function_signature(*f_iter) << ";" << endl; } indent_down(); f_service_ << "}" << endl << endl; } /** * Generates a REST interface */ void t_php_generator::generate_service_rest(t_service* tservice) { 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_ << "class " << service_name_ << "Rest" << extends_if << " {" << endl; indent_up(); if (extends.empty()) { f_service_ << indent() << "protected $impl_;" << endl << endl; } f_service_ << indent() << "public function __construct($impl) {" << endl << indent() << " $this->impl_ = $impl;" << endl << indent() << "}" << endl << endl; vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { indent(f_service_) << "public function " << (*f_iter)->get_name() << "($request) {" << endl; indent_up(); const vector& args = (*f_iter)->get_arglist()->get_members(); vector::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_ << indent() << "$" << (*a_iter)->get_name() << " = " << cast << "(!empty(" << req << ") && (" << req << " !== 'false'));" << endl; } else { f_service_ << indent() << "$" << (*a_iter)->get_name() << " = isset(" << req << ") ? " << cast << req << " : null;" << endl; } if (atype->is_string() && ((t_base_type*)atype)->is_string_list()) { f_service_ << indent() << "$" << (*a_iter)->get_name() << " = explode(',', $" << (*a_iter)->get_name() << ");" << endl; } else if (atype->is_map() || atype->is_list()) { f_service_ << indent() << "$" << (*a_iter)->get_name() << " = json_decode($" << (*a_iter)->get_name() << ", true);" << endl; } else if (atype->is_set()) { f_service_ << 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_ << 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_ << indent() << "return $this->impl_->" << (*f_iter)->get_name() << "(" << argument_list((*f_iter)->get_arglist(), false) << ");" << endl; indent_down(); indent(f_service_) << "}" << endl << endl; } indent_down(); f_service_ << "}" << endl << endl; } /** * Generates a service client definition. * * @param tservice The service to generate a server for. */ void t_php_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 " + php_namespace(tservice->get_extends()->get_program()) + extends + "Client"; } f_service_ << "class " << php_namespace_declaration(tservice) << "Client" << extends_client << " implements " << php_namespace(tservice->get_program()) << service_name_ << "If {" << endl; indent_up(); // Private members if (extends.empty()) { f_service_ << indent() << "protected $input_ = null;" << endl << indent() << "protected $output_ = null;" << endl << endl; f_service_ << indent() << "protected $seqid_ = 0;" << endl << endl; } // Constructor function f_service_ << indent() << "public function __construct($input, $output=null) {" << endl; if (!extends.empty()) { f_service_ << indent() << " parent::__construct($input, $output);" << endl; } else { f_service_ << indent() << " $this->input_ = $input;" << endl << indent() << " $this->output_ = $output ? $output : $input;" << endl; } f_service_ << indent() << "}" << endl << endl; // Generate client method implementations vector functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::const_iterator fld_iter; string funname = (*f_iter)->get_name(); // Open function indent(f_service_) << "public function " << function_signature(*f_iter) << endl; scope_up(f_service_); indent(f_service_) << "$this->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_ << "$this->recv_" << funname << "();" << endl; } scope_down(f_service_); f_service_ << endl; indent(f_service_) << "public function send_" << function_signature(*f_iter) << endl; scope_up(f_service_); std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_args"; f_service_ << indent() << "$args = 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() << "$bin_accel = ($this->output_ instanceof " << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');" << endl; f_service_ << indent() << "if ($bin_accel)" << endl; scope_up(f_service_); f_service_ << indent() << "thrift_protocol_write_binary($this->output_, '" << (*f_iter)->get_name() << "', " << "TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite());" << endl; scope_down(f_service_); f_service_ << indent() << "else" << endl; scope_up(f_service_); // Serialize the request header if (binary_inline_) { f_service_ << indent() << "$buff = pack('N', (0x80010000 | " << "TMessageType::CALL));" << endl << indent() << "$buff .= pack('N', strlen('" << funname << "'));" << endl << indent() << "$buff .= '" << funname << "';" << endl << indent() << "$buff .= pack('N', $this->seqid_);" << endl; } else { f_service_ << indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name() << "', " << "TMessageType::CALL, $this->seqid_);" << endl; } // Write to the stream if (binary_inline_) { f_service_ << indent() << "$args->write($buff);" << endl << indent() << "$this->output_->write($buff);" << endl << indent() << "$this->output_->flush();" << endl; } else { f_service_ << indent() << "$args->write($this->output_);" << endl << indent() << "$this->output_->writeMessageEnd();" << endl << indent() << "$this->output_->getTransport()->flush();" << endl; } scope_down(f_service_); scope_down(f_service_); 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_ << endl << indent() << "public function " << function_signature(&recv_function) << endl; scope_up(f_service_); f_service_ << indent() << "$bin_accel = ($this->input_ instanceof " << "TBinaryProtocolAccelerated)" << " && function_exists('thrift_protocol_read_binary');" << endl; f_service_ << indent() << "if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, '" << resultname << "', $this->input_->isStrictRead());" << endl; f_service_ << indent() << "else" << endl; scope_up(f_service_); f_service_ << 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_ << 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_, &ffname, "", true); generate_deserialize_field(f_service_, &fseqid, "", true); } else { f_service_ << indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" << endl << indent() << "if ($mtype == " << "TMessageType::EXCEPTION) {" << endl << indent() << " $x = new " << "TApplicationException();" << endl << indent() << " $x->read($this->input_);" << endl << indent() << " $this->input_->readMessageEnd();" << endl << indent() << " throw $x;" << endl << indent() << "}" << endl; } f_service_ << indent() << "$result = new " << resultname << "();" << endl << indent() << "$result->read($this->input_);" << endl; if (!binary_inline_) { f_service_ << indent() << "$this->input_->readMessageEnd();" << endl; } scope_down(f_service_); // Careful, only return result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << indent() << "if ($result->success !== null) {" << endl << indent() << " return $result->success;" << endl << indent() << "}" << endl; } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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; } // 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() << "throw new \\Exception(\"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; } // Close function scope_down(f_service_); f_service_ << endl; } } indent_down(); f_service_ << "}" << endl << endl; } /** * Deserializes a field of any type. */ void t_php_generator::generate_deserialize_field(ofstream &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_BYTE: 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_BYTE: 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(ofstream &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(ofstream &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_byte, ktype); t_field fvtype(g_type_byte, vtype); t_field fetype(g_type_byte, 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; 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 (!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(ofstream &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(ofstream &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); indent(out) << "if (is_scalar($" << elem << ")) {" << endl; indent(out) << " $" << prefix << "[$" << elem << "] = true;" << endl; indent(out) << "} else {" << endl; indent(out) << " $" << prefix << " []= $" << elem << ";" << endl; indent(out) << "}" << endl; } void t_php_generator::generate_deserialize_list_element(ofstream &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(ofstream &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_BYTE: 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_BYTE: 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(ofstream &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(ofstream &out, t_type* ttype, string prefix) { scope_up(out); 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; } } scope_up(out); if (ttype->is_map()) { string kiter = tmp("kiter"); string viter = tmp("viter"); indent(out) << "foreach ($" << prefix << " as " << "$" << kiter << " => $" << viter << ")" << 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"); string iter_val = tmp("iter"); indent(out) << "foreach ($" << prefix << " as $" << iter << " => $" << iter_val << ")" << endl; scope_up(out); indent(out) << "if (is_scalar($" << iter_val << ")) {" << endl; generate_serialize_set_element(out, (t_set*)ttype, iter); indent(out) << "} else {" << endl; generate_serialize_set_element(out, (t_set*)ttype, iter_val); indent(out) << "}" << endl; scope_down(out); } else if (ttype->is_list()) { string iter = tmp("iter"); indent(out) << "foreach ($" << prefix << " as $" << iter << ")" << endl; scope_up(out); generate_serialize_list_element(out, (t_list*)ttype, iter); scope_down(out); } 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; } } scope_down(out); } /** * Serializes the members of a map. * */ void t_php_generator::generate_serialize_map_element(ofstream &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(ofstream &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(ofstream &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_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_BYTE: 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 addStructSignature) { string result = ""; const vector& fields = tstruct->get_members(); vector::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(addStructSignature && type->is_struct()) { string className = php_namespace(type->get_program()) + php_namespace_directory("Definition", false) + classify(type->get_name()); result += className + " "; } 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_BYTE: 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_BYTE: 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(); } 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" " rest: Generate PHP REST processors\n" ) thrift-compiler_0.9.1/cpp/src/generate/t_ocaml_generator.cc0000644000175000017500000017153512203157755024660 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "t_oop_generator.h" #include "platform.h" #include "version.h" using std::ios; 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 /** * OCaml code generator. * */ class t_ocaml_generator : public t_oop_generator { public: t_ocaml_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-ocaml"; } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_program (); void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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::ofstream& out, t_struct* tstruct, bool is_xception=false); void generate_ocaml_struct_member(std::ofstream& out, string tname, t_field* tmember); void generate_ocaml_struct_sig(std::ofstream& out, t_struct* tstruct, bool is_exception); void generate_ocaml_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_ocaml_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_ocaml_function_helpers(t_function* tfunction); void generate_ocaml_method_copy(std::ofstream& out, const vector& members); void generate_ocaml_member_copy(std::ofstream& 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::ofstream &out, t_field* tfield, std::string prefix); void generate_deserialize_struct (std::ofstream &out, t_struct* tstruct); void generate_deserialize_container (std::ofstream &out, t_type* ttype); void generate_deserialize_set_element (std::ofstream &out, t_set* tset); void generate_deserialize_list_element (std::ofstream &out, t_list* tlist, std::string prefix=""); void generate_deserialize_type (std::ofstream &out, t_type* type); void generate_serialize_field (std::ofstream &out, t_field* tfield, std::string name= ""); void generate_serialize_struct (std::ofstream &out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream &out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream &out, t_map* tmap, std::string kiter, std::string viter); void generate_serialize_set_element (std::ofstream &out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream &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 */ std::ofstream f_types_; std::ofstream f_consts_; std::ofstream f_service_; std::ofstream f_types_i_; std::ofstream 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 enums = program_->get_enums(); vector::iterator en_iter; for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { generate_enum(*en_iter); } // Generate structs vector structs = program_->get_structs(); vector::iterator st_iter; for (st_iter = structs.begin(); st_iter != structs.end(); ++st_iter) { generate_struct(*st_iter); } // Generate xceptions vector xceptions = program_->get_xceptions(); vector::iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { generate_xception(*x_iter); } // Generate typedefs vector typedefs = program_->get_typedefs(); vector::iterator td_iter; for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { generate_typedef(*td_iter); } // Generate services vector services = program_->get_services(); vector::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 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 constants = tenum->get_constants(); vector::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_BYTE: 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 constants = tenum->get_constants(); vector::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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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& val = value->get_list(); vector::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(ofstream& out, const vector& members) { vector::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(ofstream& 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(ofstream& out, t_struct* tstruct, bool is_exception) { const vector& members = tstruct->get_members(); vector::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(ofstream& 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 _"<raise (Field_empty \""< " << 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(ofstream& out, t_struct* tstruct, bool is_exception) { const vector& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::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 <<","<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 "< ());" << endl; indent(out) << "iprot#readStructEnd;" << endl; indent(out) << str << endl << endl; indent_down(); indent_down(); } void t_ocaml_generator::generate_ocaml_struct_writer(ofstream& out, t_struct* tstruct) { string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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 \""<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 functions = tservice->get_functions(); vector::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& fields = xs->get_members(); vector::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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::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() << "\", 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& xceptions = xs->get_members(); if (!(*f_iter)->get_returntype()->is_void() || xceptions.size() > 0) { res = "result"; } f_service_ << indent() << "let "<get_returntype()->is_void()) { f_service_ << indent() << "match result#get_success with Some v -> v | None -> (" << endl; indent_up(); } vector::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 functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::const_iterator f_iter; string args = "args"; if(fields.size() == 0){ args="_"; } f_service_ << indent() << "let "<get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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" <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(ofstream &out, t_field* tfield, string prefix){ t_type* type = tfield->get_type(); string name = decapitalize(tfield->get_name()); indent(out) << prefix << "#set_"<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_BYTE: 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()) { string ename = capitalize(type->get_name()); out << "(" <get_name().c_str()); } } /** * Generates an unserializer for a struct, calling read() */ void t_ocaml_generator::generate_deserialize_struct(ofstream &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(ofstream &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_byte, ktype); t_field fvtype(g_type_byte, vtype); t_field fetype(g_type_byte, etype); out << endl; indent_up(); // Declare variables, read header if (ttype->is_map()) { indent(out) << "(let ("<is_set()) { indent(out) << "(let ("<get_elem_type()); out << " true" << endl; indent_down(); indent(out) << "done; iprot#readSetEnd; "<is_list()) { indent(out) << "(let ("< "; generate_deserialize_type(out,((t_list*)ttype)->get_elem_type()); out << "))) in" << endl; indent_up(); indent(out) << "iprot#readListEnd; "<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_BYTE: 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("<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(ofstream &out, t_struct* tstruct, string prefix) { (void) tstruct; indent(out) << prefix << "#write(oprot)"; } void t_ocaml_generator::generate_serialize_container(ofstream &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 "< 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 "< 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 "< "; 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(ofstream &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(ofstream &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(ofstream &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& fields = tfunc->get_arglist()->get_members(); vector::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& fields = tstruct->get_members(); vector::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_BYTE: 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_BYTE: 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", "") thrift-compiler_0.9.1/cpp/src/generate/t_hs_generator.cc0000644000175000017500000014041112203157755024164 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "t_oop_generator.h" #include "platform.h" #include "version.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 /** * Haskell code generator. * */ class t_hs_generator : public t_oop_generator { public: t_hs_generator(t_program* program, const map& parsed_options, const string& option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-hs"; } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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 (ofstream &out, t_struct* tstruct, bool is_xception = false, bool helper = false); void generate_hs_struct_reader (ofstream& out, t_struct* tstruct); void generate_hs_struct_writer (ofstream& out, t_struct* tstruct); void generate_hs_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_server (t_service* tservice); void generate_process_function (t_service* tservice, t_function* tfunction); /** * Serialization constructs */ void generate_deserialize_field (ofstream &out, t_field* tfield, string prefix); void generate_deserialize_struct (ofstream &out, t_struct* tstruct); void generate_deserialize_container (ofstream &out, t_type* ttype); void generate_deserialize_set_element (ofstream &out, t_set* tset); void generate_deserialize_list_element (ofstream &out, t_list* tlist, string prefix = ""); void generate_deserialize_type (ofstream &out, t_type* type); void generate_serialize_field (ofstream &out, t_field* tfield, string name = ""); void generate_serialize_struct (ofstream &out, t_struct* tstruct, string prefix = ""); void generate_serialize_container (ofstream &out, t_type* ttype, string prefix = ""); void generate_serialize_map_element (ofstream &out, t_map* tmap, string kiter, string viter); void generate_serialize_set_element (ofstream &out, t_set* tmap, string iter); void generate_serialize_list_element (ofstream &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 function_type(t_function* tfunc, bool options = false, bool io = false, bool method = false); string type_to_enum(t_type* ttype); string render_hs_type(t_type* type, bool needs_parens); private: ofstream f_types_; ofstream f_consts_; ofstream f_service_; ofstream f_iface_; ofstream 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 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& includes = program_->get_includes(); string result = string( "import Prelude ( Bool(..), Enum, Double, String, Maybe(..),\n" " Eq, Show, Ord,\n" " return, length, IO, fromIntegral, fromEnum, toEnum,\n" " (.), (&&), (||), (==), (++), ($), (-) )\n" "\n" "import Control.Exception\n" "import Data.ByteString.Lazy\n" "import Data.Hashable\n" "import Data.Int\n" "import Data.Text.Lazy ( Text )\n" "import qualified Data.Text.Lazy as TL\n" "import Data.Typeable ( Typeable )\n" "import qualified Data.HashMap.Strict as Map\n" "import qualified Data.HashSet as Set\n" "import qualified Data.Vector as Vector\n" "\n" "import Thrift\n" "import Thrift.Types ()\n" "\n"); for (size_t i = 0; i < includes.size(); ++i) result += "import qualified " + capitalize(includes[i]->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 constants = tenum->get_constants(); vector::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 (Show,Eq, Typeable, Ord)" << endl; indent_down(); string ename = capitalize(tenum->get_name()); indent(f_types_) << "instance 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_) << "_ -> throw ThriftException" << endl; indent_down(); indent_down(); indent(f_types_) << "instance Hashable " << ename << " where" << endl; indent_up(); indent(f_types_) << "hashWithSalt salt = hashWithSalt salt . fromEnum" << 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) { 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_BYTE: out << "(" << value->get_integer() << " :: Int8)"; break; case t_base_type::TYPE_I16: out << "(" << value->get_integer() << " :: Int16)"; break; case t_base_type::TYPE_I32: out << "(" << value->get_integer() << " :: Int32)"; break; case t_base_type::TYPE_I64: out << "(" << value->get_integer() << " :: Int64)"; 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 constants = tenum->get_constants(); vector::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((*c_iter)->get_name()); break; } } } else if (type->is_struct() || type->is_xception()) { string cname = type_name(type); indent(out) << cname << "{"; const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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(); string fname = v_iter->first->get_string(); string const_value = render_const_value(field_type, v_iter->second); out << (first ? "" : ","); out << "f_" << cname << "_" << fname << " = Just (" << const_value << ")"; first = false; } 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(); const map& val = value->get_map(); map::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& val = value->get_list(); vector::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(ofstream& out, t_struct* tstruct, bool is_exception, bool helper) { (void) helper; string tname = type_name(tstruct); string name = tstruct->get_name(); const vector& members = tstruct->get_members(); vector::const_iterator m_iter; indent(out) << "data " << tname << " = " << tname; if (members.size() > 0) { out << "{"; bool first = true; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { string mname = (*m_iter)->get_name(); out << (first ? "" : ","); out << "f_" << tname << "_" << mname << " :: Maybe " << render_hs_type((*m_iter)->get_type(), true); first = false; } out << "}"; } out << " deriving (Show,Eq,Typeable)" << endl; if (is_exception) out << "instance Exception " << tname << endl; indent(out) << "instance Hashable " << tname << " where" << endl; indent_up(); indent(out) << "hashWithSalt salt record = salt"; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { string mname = (*m_iter)->get_name(); indent(out) << " `hashWithSalt` " << "f_" << tname << "_" << mname << " record"; } indent(out) << endl; indent_down(); generate_hs_struct_writer(out, tstruct); generate_hs_struct_reader(out, tstruct); } /** * Generates the read method for a struct */ void t_hs_generator::generate_hs_struct_reader(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; string sname = type_name(tstruct); string str = tmp("_str"); string t = tmp("_t"); string id = tmp("_id"); indent(out) << "read_" << sname << "_fields iprot record = do" << endl; indent_up(); // Read beginning field marker indent(out) << "(_," << t << "," << id << ") <- readFieldBegin iprot" << endl; // Check for field STOP marker and break indent(out) << "if " << t << " == T_STOP then return record else" << endl; indent_up(); indent(out) << "case " << id << " of " << endl; indent_up(); // Generate deserialization code for known cases for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { int32_t key = (*f_iter)->get_key(); string etype = type_to_enum((*f_iter)->get_type()); indent(out) << key << " -> " << "if " << t << " == " << etype << " then do" << endl; indent_up(); indent(out) << "s <- "; generate_deserialize_field(out, *f_iter,str); out << endl; string fname = decapitalize((*f_iter)->get_name()); indent(out) << "read_" << sname << "_fields iprot record{f_" << sname << "_" << fname << "=Just s}" << endl; indent(out) << "else do" << endl; indent_up(); indent(out) << "skip iprot " << t << endl; indent(out) << "read_" << sname << "_fields iprot record" << endl; indent_down(); indent_down(); } // In the default case we skip the field indent(out) << "_ -> do" << endl; indent_up(); indent(out) << "skip iprot " << t << endl; indent(out) << "readFieldEnd iprot" << endl; indent(out) << "read_" << sname << "_fields iprot record" << endl; indent_down(); indent_down(); indent_down(); indent_down(); // read indent(out) << "read_" << sname << " iprot = do" << endl; indent_up(); indent(out) << "_ <- readStructBegin iprot" << endl; indent(out) << "record <- read_" << sname << "_fields iprot (" << sname << "{"; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { out << (first ? "" : ","); out << "f_" << sname << "_" << decapitalize((*f_iter)->get_name()) << "=Nothing"; first = false; } out << "})" << endl; indent(out) << "readStructEnd iprot" << endl; indent(out) << "return record" << endl; indent_down(); } void t_hs_generator::generate_hs_struct_writer(ofstream& out, t_struct* tstruct) { string name = type_name(tstruct); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; string str = tmp("_str"); string f = tmp("_f"); indent(out) << "write_" << name << " oprot record = do" << endl; indent_up(); indent(out) << "writeStructBegin oprot \"" << name << "\"" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { // Write field header string mname = (*f_iter)->get_name(); indent(out) << "case f_" << name << "_" << mname << " record of {Nothing -> return (); Just _v -> do" << endl; indent_up(); indent(out) << "writeFieldBegin oprot (\"" << (*f_iter)->get_name() << "\"," << type_to_enum((*f_iter)->get_type()) << "," << (*f_iter)->get_key() << ")" << endl; // Write field contents indent(out); generate_serialize_field(out, *f_iter, "_v"); out << endl; // Write field closer indent(out) << "writeFieldEnd oprot}" << endl; indent_down(); } // Write the struct map indent(out) << "writeFieldStop oprot" << endl; indent(out) << "writeStructEnd oprot" << endl; indent_down(); } /** * 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 functions = tservice->get_functions(); vector::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_, 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& fields = xs->get_members(); vector::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); } /** * 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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 Data.IORef" << 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 = 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& fields = arg_struct->get_members(); vector::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_" + decapitalize((*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 <- readIORef seq" << endl; string argsname = capitalize((*f_iter)->get_name() + "_args"); // Serialize the request header string fname = (*f_iter)->get_name(); indent(f_client_) << "writeMessageBegin op (\"" << fname << "\", M_CALL, seqn)" << endl; indent(f_client_) << "write_" << argsname << " op (" << argsname << "{"; bool first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { string fieldname = (*fld_iter)->get_name(); f_client_ << (first ? "" : ","); f_client_ << "f_" << argsname << "_" << fieldname << "=Just arg_" << fieldname; first = false; } f_client_ << "})" << endl; // Write to the stream indent(f_client_) << "writeMessageEnd op" << endl; indent(f_client_) << "tFlush (getTransport op)" << endl; 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(); // TODO(mcslee): Validate message reply here, seq ids etc. indent(f_client_) << "(fname, mtype, rseqid) <- readMessageBegin ip" << endl; indent(f_client_) << "if mtype == M_EXCEPTION then do" << endl; indent(f_client_) << " x <- readAppExn ip" << endl; indent(f_client_) << " readMessageEnd ip" << endl; indent(f_client_) << " throw x" << endl; indent(f_client_) << " else return ()" << endl; t_struct* xs = (*f_iter)->get_xceptions(); const vector& xceptions = xs->get_members(); indent(f_client_) << "res <- read_" << resultname << " ip" << endl; indent(f_client_) << "readMessageEnd ip" << endl; // Careful, only return _result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { indent(f_client_) << "case f_" << resultname << "_success res of" << endl; indent_up(); indent(f_client_) << "Just v -> return v" << endl; indent(f_client_) << "Nothing -> do" << endl; indent_up(); } vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { string xname = (*x_iter)->get_name(); indent(f_client_) << "case f_" << resultname << "_" << xname << " res of" << endl; indent_up(); indent(f_client_) << "Nothing -> return ()" << endl; indent(f_client_) << "Just _v -> throw _v" << endl; indent_down(); } // Careful, only return _result if not a void function if ((*f_iter)->get_returntype()->is_void()) { indent(f_client_) << "return ()" << endl; } else { string tname = (*f_iter)->get_name(); indent(f_client_) << "throw (AppExn AE_MISSING_RESULT \"" << tname << " failed: unknown result\")" << endl; indent_down(); indent_down(); } // Close function 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 functions = tservice->get_functions(); vector::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_) << "skip iprot T_STRUCT" << endl; indent(f_service_) << "readMessageEnd iprot" << endl; indent(f_service_) << "writeMessageBegin oprot (name,M_EXCEPTION,seqid)" << endl; indent(f_service_) << "writeAppExn oprot (AppExn AE_UNKNOWN_METHOD (\"Unknown function \" ++ TL.unpack name))" << endl; indent(f_service_) << "writeMessageEnd oprot" << endl; indent(f_service_) << "tFlush (getTransport oprot)" << endl; indent_down(); } indent_down(); // Generate the server implementation indent(f_service_) << "process handler (iprot, oprot) = do" << endl; indent_up(); indent(f_service_) << "(name, typ, seqid) <- readMessageBegin iprot" << endl; indent(f_service_) << "proc_ handler (iprot,oprot) (name,typ,seqid)" << endl; indent(f_service_) << "return True" << endl; indent_down(); } /** * 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& fields = arg_struct->get_members(); vector::const_iterator f_iter; indent(f_service_) << "args <- read_" << argsname << " iprot" << endl; indent(f_service_) << "readMessageEnd iprot" << endl; t_struct* xs = tfunction->get_xceptions(); const vector& xceptions = xs->get_members(); vector::const_iterator x_iter; size_t n = xceptions.size(); if (!tfunction->is_oneway()) { if (!tfunction->get_returntype()->is_void()) n++; indent(f_service_) << "rs <- return (" << resultname; for(size_t i = 0; i < n; i++) f_service_ << " Nothing"; f_service_ << ")" << endl; } indent(f_service_) << "res <- "; // Try block for a function with exceptions if (xceptions.size() > 0) { for(size_t i = 0; i < xceptions.size(); i++) { f_service_ << "(Control.Exception.catch" << endl; indent_up(); indent(f_service_); } } f_service_ << "(do" << endl; indent_up(); indent(f_service_); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) f_service_ << "res <- "; f_service_ << "Iface." << decapitalize(tfunction->get_name()) << " handler"; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) f_service_ << " (f_" << argsname << "_" << (*f_iter)->get_name() << " args)"; if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { f_service_ << endl; indent(f_service_) << "return rs{f_" << resultname << "_success= Just res}"; } else if (!tfunction->is_oneway()) { f_service_ << endl; indent(f_service_) << "return rs"; } f_service_ << ")" << endl; indent_down(); if (xceptions.size() > 0 && !tfunction->is_oneway()) { for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { indent(f_service_) << "(\\e -> " << endl; indent_up(); if (!tfunction->is_oneway()) { indent(f_service_) << "return rs{f_" << resultname << "_" << (*x_iter)->get_name() << " =Just e}"; } else { indent(f_service_) << "return ()"; } f_service_ << "))" << endl; indent_down(); indent_down(); } } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { indent(f_service_) << "return ()" << endl; indent_down(); return; } indent(f_service_ ) << "writeMessageBegin oprot (\"" << tfunction->get_name() << "\", M_REPLY, seqid);" << endl; indent(f_service_ ) << "write_" << resultname << " oprot res" << endl; indent(f_service_ ) << "writeMessageEnd oprot" << endl; indent(f_service_ ) << "tFlush (getTransport oprot)" << endl; // Close function indent_down(); } /** * Deserializes a field of any type. */ void t_hs_generator::generate_deserialize_field(ofstream &out, t_field* tfield, string prefix) { (void) prefix; t_type* type = tfield->get_type(); generate_deserialize_type(out,type); } /** * Deserializes a field of any type. */ void t_hs_generator::generate_deserialize_type(ofstream &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()) { 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 << (((t_base_type*)type)->is_binary() ? "readBinary" : "readString"); break; case t_base_type::TYPE_BOOL: out << "readBool"; break; case t_base_type::TYPE_BYTE: 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); } out << " iprot"; } else if (type->is_enum()) { string ename = capitalize(type->get_name()); out << "(do {i <- readI32 iprot; return $ toEnum $ fromIntegral i})"; } 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_hs_generator::generate_deserialize_struct(ofstream &out, t_struct* tstruct) { string name = capitalize(tstruct->get_name()); out << "(read_" << name << " iprot)"; } /** * Serialize a container by writing out the header followed by * data and then a footer. */ void t_hs_generator::generate_deserialize_container(ofstream &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_byte, ktype); t_field fvtype(g_type_byte, vtype); t_field fetype(g_type_byte, etype); // Declare variables, read header if (ttype->is_map()) { out << "(let {f 0 = return []; f n = do {k <- "; generate_deserialize_type(out,((t_map*)ttype)->get_key_type()); out << "; v <- "; generate_deserialize_type(out,((t_map*)ttype)->get_val_type()); out << ";r <- f (n-1); return $ (k,v):r}} in do {(" << ktype << "," << vtype << "," << size << ") <- readMapBegin iprot; l <- f " << size << "; return $ Map.fromList l})"; } else if (ttype->is_set()) { out << "(let {f 0 = return []; f n = do {v <- "; generate_deserialize_type(out,((t_map*)ttype)->get_key_type()); out << ";r <- f (n-1); return $ v:r}} in do {(" << etype << "," << size << ") <- readSetBegin iprot; l <- f " << size << "; return $ Set.fromList l})"; } else if (ttype->is_list()) { out << "(let f n = Vector.replicateM (fromIntegral n) ("; generate_deserialize_type(out,((t_map*)ttype)->get_key_type()); out << ") in do {(" << etype << "," << size << ") <- readListBegin iprot; f " << size << "})"; } } /** * 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_field(ofstream &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()) { 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 << (((t_base_type*)type)->is_binary() ? "writeBinary" : "writeString") << " oprot " << name; break; case t_base_type::TYPE_BOOL: out << "writeBool oprot " << name; break; case t_base_type::TYPE_BYTE: out << "writeByte oprot " << name; break; case t_base_type::TYPE_I16: out << "writeI16 oprot " << name; break; case t_base_type::TYPE_I32: out << "writeI32 oprot " << name; break; case t_base_type::TYPE_I64: out << "writeI64 oprot " << name; break; case t_base_type::TYPE_DOUBLE: out << "writeDouble oprot " << name; break; default: throw "compiler error: no hs name for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { string ename = capitalize(type->get_name()); out << "writeI32 oprot (fromIntegral $ fromEnum " << name << ")"; } } else { printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", 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_hs_generator::generate_serialize_struct(ofstream &out, t_struct* tstruct, string prefix) { out << type_name(tstruct, "write_") << " oprot " << prefix; } void t_hs_generator::generate_serialize_container(ofstream &out, t_type* ttype, string prefix) { if (ttype->is_map()) { string k = tmp("_kiter"); string v = tmp("_viter"); out << "(let {f [] = return (); f ((" << k << "," << v << "):t) = do {"; generate_serialize_map_element(out, (t_map*)ttype, k, v); out << ";f t}} in do {writeMapBegin oprot (" << type_to_enum(((t_map*)ttype)->get_key_type()) << "," << type_to_enum(((t_map*)ttype)->get_val_type()) << ",fromIntegral $ Map.size " << prefix << "); f (Map.toList " << prefix << ");writeMapEnd oprot})"; } else if (ttype->is_set()) { string v = tmp("_viter"); out << "(let {f [] = return (); f (" << v << ":t) = do {"; generate_serialize_set_element(out, (t_set*)ttype, v); out << ";f t}} in do {writeSetBegin oprot (" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ",fromIntegral $ Set.size " << prefix << "); f (Set.toList " << prefix << ");writeSetEnd oprot})"; } else if (ttype->is_list()) { string v = tmp("_viter"); out << "(let f = Vector.mapM_ (\\" << v << " -> "; generate_serialize_list_element(out, (t_list*)ttype, v); out << ") in do {writeListBegin oprot (" << type_to_enum(((t_list*)ttype)->get_elem_type()) << ",fromIntegral $ Vector.length " << prefix << "); f " << prefix << ";writeListEnd oprot})"; } } /** * Serializes the members of a map. * */ void t_hs_generator::generate_serialize_map_element(ofstream &out, t_map* tmap, string kiter, string viter) { t_field kfield(tmap->get_key_type(), kiter); out << "do {"; generate_serialize_field(out, &kfield); out << ";"; t_field vfield(tmap->get_val_type(), viter); generate_serialize_field(out, &vfield); out << "}"; } /** * Serializes the members of a set. */ void t_hs_generator::generate_serialize_set_element(ofstream &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_hs_generator::generate_serialize_list_element(ofstream &out, t_list* tlist, string iter) { t_field efield(tlist->get_elem_type(), iter); generate_serialize_field(out, &efield); } string t_hs_generator::function_type(t_function* tfunc, bool options, bool io, bool method) { string result = ""; const vector& fields = tfunc->get_arglist()->get_members(); vector::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (options) result += "Maybe "; result += render_hs_type((*f_iter)->get_type(), options); result += " -> "; } if (fields.empty() && !method) result += "() -> "; if (io) result += "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()); } /** * 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_VOID"; case t_base_type::TYPE_STRING: return "T_STRING"; case t_base_type::TYPE_BOOL: return "T_BOOL"; case t_base_type::TYPE_BYTE: 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() || 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(); } /** * 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 (((t_base_type*)type)->is_binary() ? "ByteString" : "Text"); case t_base_type::TYPE_BOOL: return "Bool"; case t_base_type::TYPE_BYTE: 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"; } } else if (type->is_enum()) { return capitalize(((t_enum*)type)->get_name()); } 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; } THRIFT_REGISTER_GENERATOR(hs, "Haskell", "") thrift-compiler_0.9.1/cpp/src/generate/t_generator_registry.h0000644000175000017500000000761312203157755025272 0ustar eevanseevans00000000000000/* * 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() {} 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& 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 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) {} virtual t_generator* get_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) { return new generator(program, parsed_options, option_string); } virtual bool is_valid_namespace(const std::string& sub_namespace) { 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); typedef std::map 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 \ { \ public: \ t_##language##_generator_factory_impl() \ : t_generator_factory_impl( \ #language, long_name, doc) \ {} \ }; \ static t_##language##_generator_factory_impl _registerer; #endif thrift-compiler_0.9.1/cpp/src/generate/t_java_generator.cc0000644000175000017500000051317112203157755024502 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "platform.h" #include "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 /** * Java code generator. * */ class t_java_generator : public t_oop_generator { public: t_java_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("beans"); bean_style_ = (iter != parsed_options.end()); iter = parsed_options.find("private-members"); private_members_ = (iter != parsed_options.end()); iter = parsed_options.find("nocamel"); nocamel_style_ = (iter != parsed_options.end()); iter = parsed_options.find("hashcode"); gen_hash_code_ = (iter != parsed_options.end()); iter = parsed_options.find("android_legacy"); android_legacy_ = (iter != parsed_options.end()); iter = parsed_options.find("sorted_containers"); sorted_containers_ = (iter != parsed_options.end()); iter = parsed_options.find("java5"); java5_ = (iter != parsed_options.end()); if (java5_) { android_legacy_ = true; } out_dir_base_ = (bean_style_ ? "gen-javabean" : "gen-java"); } /** * Init and close methods */ void init_generator(); void close_generator(); void generate_consts(std::vector consts); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_struct (t_struct* tstruct); void generate_union (t_struct* tunion); void generate_xception(t_struct* txception); void generate_service (t_service* tservice); void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval=false); std::string render_const_value(std::ofstream& 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::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false); void generate_java_struct_equality(std::ofstream& out, t_struct* tstruct); void generate_java_struct_compare_to(std::ofstream& out, t_struct* tstruct); void generate_java_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_java_validator(std::ofstream& out, t_struct* tstruct); void generate_java_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_java_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_java_struct_tostring(std::ofstream& out, t_struct* tstruct); void generate_java_struct_clear(std::ofstream& out, t_struct* tstruct); void generate_java_struct_write_object(std::ofstream& out, t_struct* tstruct); void generate_java_struct_read_object(std::ofstream& out, t_struct* tstruct); void generate_java_meta_data_map(std::ofstream& out, t_struct* tstruct); void generate_field_value_meta_data(std::ofstream& out, t_type* type); std::string get_java_type_string(t_type* type); void generate_java_struct_field_by_id(ofstream& 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::ofstream& out, t_struct* tstruct); void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct); void generate_java_bean_boilerplate(std::ofstream& 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(ofstream& 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(ofstream& out, t_struct* tstruct); void generate_union_getters_and_setters(ofstream& out, t_struct* tstruct); void generate_union_is_set_methods(ofstream& out, t_struct* tstruct); void generate_union_abstract_methods(ofstream& out, t_struct* tstruct); void generate_check_type(ofstream& out, t_struct* tstruct); void generate_standard_scheme_read_value(ofstream& out, t_struct* tstruct); void generate_standard_scheme_write_value(ofstream& out, t_struct* tstruct); void generate_tuple_scheme_read_value(ofstream& out, t_struct* tstruct); void generate_tuple_scheme_write_value(ofstream& out, t_struct* tstruct); void generate_get_field_desc(ofstream& out, t_struct* tstruct); void generate_get_struct_desc(ofstream& out, t_struct* tstruct); void generate_get_field_name(ofstream& out, t_struct* tstruct); void generate_union_comparisons(ofstream& out, t_struct* tstruct); void generate_union_hashcode(ofstream& out, t_struct* tstruct); void generate_scheme_map(ofstream& out, t_struct* tstruct); void generate_standard_writer(ofstream& out, t_struct* tstruct, bool is_result); void generate_standard_reader(ofstream& out, t_struct* tstruct); void generate_java_struct_standard_scheme(ofstream& out, t_struct* tstruct, bool is_result); void generate_java_struct_tuple_scheme(ofstream& out, t_struct* tstruct); void generate_java_struct_tuple_reader(ofstream& out, t_struct* tstruct); void generate_java_struct_tuple_writer(ofstream& out, t_struct* tstruct); /** * Serialization constructs */ void generate_deserialize_field (std::ofstream& out, t_field* tfield, std::string prefix="", bool has_metadata = true); void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix="", bool has_metadata = true); void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix="", bool has_metadata = true); void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix="", bool has_metadata = true); void generate_deserialize_list_element (std::ofstream& out, t_list* tlist, std::string prefix="", bool has_metadata = true); void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix="", bool has_metadata = true); void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix="", bool has_metadata = true); void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter, std::string map, bool has_metadata = true); void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter, bool has_metadata = true); void generate_serialize_list_element (std::ofstream& out, t_list* tlist, std::string iter, bool has_metadata = true); void generate_java_doc (std::ofstream& out, t_field* field); void generate_java_doc (std::ofstream& out, t_doc* tdoc); void generate_java_doc (std::ofstream& out, t_function* tdoc); void generate_java_docstring_comment (std::ofstream &out, string contents); void generate_deep_copy_container(std::ofstream& 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::ofstream& 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_type_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, 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); std::string get_enum_class_name(t_type* type); void generate_struct_desc(ofstream& out, t_struct* tstruct); void generate_field_descs(ofstream& out, t_struct* tstruct); void generate_field_name_constants(ofstream& 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(); } std::string constant_name(std::string name); private: /** * File streams */ std::string package_name_; std::ofstream f_service_; std::string package_dir_; bool bean_style_; bool private_members_; bool nocamel_style_; bool gen_hash_code_; bool android_legacy_; bool java5_; bool sorted_containers_; }; /** * 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 ""; } /** * Prints standard java imports * * @return List of imports for Java types that are used in here */ string t_java_generator::java_type_imports() { string hash_builder; string tree_set_and_map; if (gen_hash_code_) { hash_builder = "import org.apache.commons.lang3.builder.HashCodeBuilder;\n"; } if (sorted_containers_) { tree_set_and_map = string() + "import java.util.TreeSet;\n" + "import java.util.TreeMap;\n"; } return string() + hash_builder + "import org.apache.thrift.scheme.IScheme;\n" + "import org.apache.thrift.scheme.SchemeFactory;\n" + "import org.apache.thrift.scheme.StandardScheme;\n\n" + "import org.apache.thrift.scheme.TupleScheme;\n" + "import org.apache.thrift.protocol.TTupleProtocol;\n" + "import org.apache.thrift.protocol.TProtocolException;\n" + "import org.apache.thrift.EncodingUtils;\n" + "import org.apache.thrift.TException;\n" + "import org.apache.thrift.async.AsyncMethodCallback;\n"+ "import org.apache.thrift.server.AbstractNonblockingServer.*;\n"+ "import java.util.List;\n" + "import java.util.ArrayList;\n" + "import java.util.Map;\n" + "import java.util.HashMap;\n" + "import java.util.EnumMap;\n" + "import java.util.Set;\n" + "import java.util.HashSet;\n" + "import java.util.EnumSet;\n" + tree_set_and_map + "import java.util.Collections;\n" + "import java.util.BitSet;\n" + "import java.nio.ByteBuffer;\n" "import java.util.Arrays;\n" + "import org.slf4j.Logger;\n" + "import org.slf4j.LoggerFactory;\n\n"; } /** * 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) { // Make output file string f_enum_name = package_dir_+"/"+make_valid_java_filename(tenum->get_name())+".java"; ofstream f_enum; f_enum.open(f_enum_name.c_str()); // Comment and package it f_enum << autogen_comment() << java_package() << endl; // Add java imports f_enum << string() + "import java.util.Map;\n" + "import java.util.HashMap;\n" + "import org.apache.thrift.TEnum;" << endl << endl; generate_java_doc(f_enum, tenum); indent(f_enum) << "public enum " << tenum->get_name() << " implements org.apache.thrift.TEnum "; scope_up(f_enum); vector constants = tenum->get_constants(); vector::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); 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;" <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 consts) { if (consts.empty()) { return; } string f_consts_name = package_dir_+ '/' + make_valid_java_filename(program_name_) + "Constants.java"; ofstream 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 " << make_valid_java_identifier(program_name_) << "Constants {" << endl << endl; indent_up(); vector::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::ofstream& 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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()) { 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& val = value->get_map(); map::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()) { 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& val = value->get_list(); vector::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(ofstream& 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: 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_BYTE: 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->get_program()->get_namespace("java") << "." << 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 f_struct; f_struct.open(f_struct_name.c_str()); f_struct << autogen_comment() << java_package() << java_type_imports(); 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 f_struct; f_struct.open(f_struct_name.c_str()); f_struct << autogen_comment() << java_package() << java_type_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 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(ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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, 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_base_type() && ((t_base_type*)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()) << "(ByteBuffer.wrap(value));" << endl; indent(out) << " return x;" << endl; indent(out) << "}" << endl << endl; } } } void t_java_generator::generate_union_getters_and_setters(ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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()); generate_java_doc(out, field); if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { 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) << " 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 ByteBuffer buffer" << get_cap_name("for") << get_cap_name(field->get_name()) << "() {" << endl; indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {" << endl; indent(out) << " return (ByteBuffer)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; } else { 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); if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { indent(out) << "public void set" << get_cap_name(field->get_name()) << "(byte[] value) {" << endl; indent(out) << " set" << get_cap_name(field->get_name()) << "(ByteBuffer.wrap(value));" << endl; indent(out) << "}" << endl; out << 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 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(ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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(ofstream& 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(ofstream& out, t_struct* tstruct) { indent(out) << "@Override" << endl; indent(out) << "protected void checkType(_Fields setField, Object value) throws ClassCastException {" << endl; indent_up(); indent(out) << "switch (setField) {" << endl; indent_up(); const vector& members = tstruct->get_members(); vector::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_java_generator::generate_standard_scheme_read_value(ofstream& out, t_struct* tstruct) { indent(out) << "@Override" << endl; indent(out) << "protected 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& members = tstruct->get_members(); vector::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 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(ofstream& 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& members = tstruct->get_members(); vector::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_java_generator::generate_tuple_scheme_read_value(ofstream& out, t_struct* tstruct) { indent(out) << "@Override" << endl; indent(out) << "protected 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& members = tstruct->get_members(); vector::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 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 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(ofstream& 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& members = tstruct->get_members(); vector::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_java_generator::generate_get_field_desc(ofstream& out, t_struct* tstruct) { indent(out) << "@Override" << endl; indent(out) << "protected org.apache.thrift.protocol.TField getFieldDesc(_Fields setField) {" << endl; indent_up(); const vector& members = tstruct->get_members(); vector::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_java_generator::generate_get_struct_desc(ofstream& 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(ofstream& 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) << "@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(ofstream& out, t_struct* tstruct) { (void) tstruct; if (gen_hash_code_) { indent(out) << "@Override" << endl; indent(out) << "public int hashCode() {" << endl; indent(out) << " HashCodeBuilder hcb = new HashCodeBuilder();" << endl; indent(out) << " hcb.append(this.getClass().getName());" << endl; indent(out) << " org.apache.thrift.TFieldIdEnum setField = getSetField();" << endl; indent(out) << " if (setField != null) {" << endl; indent(out) << " hcb.append(setField.getThriftFieldId());" << endl; indent(out) << " Object value = getFieldValue();" << endl; indent(out) << " if (value instanceof org.apache.thrift.TEnum) {" << endl; indent(out) << " hcb.append(((org.apache.thrift.TEnum)getFieldValue()).getValue());" << endl; indent(out) << " } else {" << endl; indent(out) << " hcb.append(value);" << endl; indent(out) << " }" << endl; indent(out) << " }" << endl; indent(out) << " return hcb.toHashCode();" << endl; indent(out) << "}"; } else { 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) << "@Override" << 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_java_generator::generate_java_struct_definition(ofstream &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 TException "; } out << "implements org.apache.thrift.TBase<" << tstruct->get_name() << ", " << tstruct->get_name() << "._Fields>, java.io.Serializable, Cloneable, Comparable<" << tstruct->get_name() << ">"; out << " "; scope_up(out); generate_struct_desc(out, tstruct); // Members are public for -java, private for -javabean const vector& members = tstruct->get_members(); vector::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; 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 BitSet __isset_bit_vector = new BitSet(" << i << ");" << endl; break; } if (optionals > 0) { std::string output_string = "private _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) { 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 other." << 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(); 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); scope_down(out); 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(ofstream& out, t_struct* tstruct) { out << indent() << "@Override" << endl << 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; const vector& members = tstruct->get_members(); vector::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_base_type() && ((t_base_type*)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; out << indent() << "@Override" << endl << indent() << "public int hashCode() {" << endl; indent_up(); if (gen_hash_code_) { indent(out) << "HashCodeBuilder builder = new HashCodeBuilder();" << 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(); string present = "true"; if (is_optional || can_be_null) { present += " && (" + generate_isset_check(*m_iter) + ")"; } indent(out) << "boolean present_" << name << " = " << present << ";" << endl; indent(out) << "builder.append(present_" << name << ");" << endl; indent(out) << "if (present_" << name << ")" << endl; if (t->is_enum()) { indent(out) << " builder.append(" << name << ".getValue());" << endl; } else { indent(out) << " builder.append(" << name << ");" << endl; } } out << endl; indent(out) << "return builder.toHashCode();" << endl; } else { indent(out) << "return 0;" << endl; } indent_down(); indent(out) << "}" << endl << endl; } void t_java_generator::generate_java_struct_compare_to(ofstream& 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& members = tstruct->get_members(); vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* field = *m_iter; indent(out) << "lastComparison = 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(ofstream& 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) << "schemes.get(iprot.getScheme()).getScheme().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(ofstream& out, t_struct* tstruct){ indent(out) << "public void validate() throws org.apache.thrift.TException {" << endl; indent_up(); const vector& fields = tstruct->get_members(); vector::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(ofstream& 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) << "schemes.get(oprot.getScheme()).getScheme().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(ofstream& 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) << "schemes.get(oprot.getScheme()).getScheme().write(oprot, this);" << endl; indent_down(); indent(out) << " }" << endl << endl; } void t_java_generator::generate_java_struct_field_by_id(ofstream& out, t_struct* tstruct) { (void) tstruct; 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(); if (type->is_base_type() && !type->is_string()) { t_base_type* base_type = (t_base_type*)type; indent(out) << "return " << type_name(type, true, false) << ".valueOf(" << (base_type->is_bool() ? "is" : "get") << cap_name << "());" << endl << endl; } else { indent(out) << "return 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) { 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_java_generator::generate_generic_field_getters_setters(std::ofstream& 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& fields = tstruct->get_members(); vector::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, Object value) {" << endl; indent(out) << " switch (field) {" << endl; out << setter_stream.str(); indent(out) << " }" << endl; indent(out) << "}" << endl << endl; // create the getter indent(out) << "public Object getFieldValue(_Fields field) {" << endl; indent_up(); indent(out) << "switch (field) {" << endl; out << getter_stream.str(); indent(out) << "}" << endl; indent(out) << "throw new 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::ofstream& out, t_struct* tstruct){ const vector& fields = tstruct->get_members(); vector::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 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 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(ofstream& out, t_struct* tstruct) { isset_type issetType = needs_isset(tstruct); const vector& fields = tstruct->get_members(); vector::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 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 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; 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(); indent(out); 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(); 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); if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { 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 ByteBuffer buffer" << get_cap_name("for") << cap_name << "() {" << endl; indent(out) << " return " << field_name << ";" << endl; indent(out) << "}" << endl << endl; } else { 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_base_type() && ((t_base_type*)type)->is_binary()) { indent(out) << "public "; if (bean_style_) { out << "void"; } else { out << type_name(tstruct); } out << " set" << cap_name << "(byte[] " << field_name << ") {" << endl; indent(out) << " set" << cap_name << "(" << field_name << " == null ? (ByteBuffer)null : ByteBuffer.wrap(" << field_name << "));" << endl; if (!bean_style_) { indent(out) << " return this;" << endl; } indent(out) << "}" << endl << endl; } indent(out) << "public "; if (bean_style_) { out << "void"; } else { out << type_name(tstruct); } 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, ""); if (!bean_style_) { indent(out) << "return this;" << endl; } 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 if(issetType == ISSET_PRIMITIVE) { indent(out) << "__isset_bitfield = 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; 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 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; 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 = 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(ofstream& out, t_struct* tstruct) { out << indent() << "@Override" << endl << indent() << "public String toString() {" << endl; indent_up(); out << indent() << "StringBuilder sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl; out << indent() << "boolean first = true;" << endl << endl; const vector& fields = tstruct->get_members(); vector::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_base_type() && ((t_base_type*)(field->get_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(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; // Static Map with fieldID -> org.apache.thrift.meta_data.FieldMetaData mappings indent(out) << "public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;" << endl; indent(out) << "static {" << endl; indent_up(); indent(out) << "Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new 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 = 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_BYTE : 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!"); 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::ofstream& out, t_type* type){ out << endl; indent_up(); indent_up(); if (type->is_struct()){ 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_list*)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 (((t_base_type*)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_type_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_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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 {" << 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 functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::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 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; } indent(f_service_) << "sendBase(\"" << 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_") + (*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() << 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& xceptions = xs->get_members(); vector::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 {" << 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 functions = tservice->get_functions(); vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string funname = (*f_iter)->get_name(); t_type* ret_type = (*f_iter)->get_returntype(); t_struct* arg_struct = (*f_iter)->get_arglist(); string funclassname = funname + "_call"; const vector& fields = arg_struct->get_members(); const std::vector& xceptions = (*f_iter)->get_xceptions()->get_members(); vector::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 {" << 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.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) + " getResult() throws "; vector::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 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; if (!(*f_iter)->is_oneway()) { indent(f_service_); if (!ret_type->is_void()) { f_service_ << "return "; } f_service_ << "(new Client(prot)).recv_" + funname + "();" << 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 functions = tservice->get_functions(); vector::iterator f_iter; // Extends stuff string extends = ""; string extends_processor = ""; if (tservice->get_extends() == NULL) { extends_processor = "org.apache.thrift.TBaseProcessor"; } else { extends = type_name(tservice->get_extends()); extends_processor = extends + ".Processor"; } // Generate the header portion indent(f_service_) << "public static class Processor extends " << extends_processor << " implements org.apache.thrift.TProcessor {" << endl; indent_up(); indent(f_service_) << "private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class.getName());" << endl; indent(f_service_) << "public Processor(I iface) {" << endl; indent(f_service_) << " super(iface, getProcessMap(new HashMap>()));" << endl; indent(f_service_) << "}" << endl << endl; indent(f_service_) << "protected Processor(I iface, Map> processMap) {" << endl; indent(f_service_) << " super(iface, getProcessMap(processMap));" << endl; indent(f_service_) << "}" << endl << endl; indent(f_service_) << "private static Map> getProcessMap(Map> 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 functions = tservice->get_functions(); vector::iterator f_iter; // Extends stuff string extends = ""; string extends_processor = ""; if (tservice->get_extends() == NULL) { extends_processor = "org.apache.thrift.TBaseAsyncProcessor"; } else { extends = type_name(tservice->get_extends()); extends_processor = extends + ".AsyncProcessor"; } // Generate the header portion indent(f_service_) << "public static class AsyncProcessor extends " << extends_processor << " {" << endl; indent_up(); indent(f_service_) << "private static final Logger LOGGER = LoggerFactory.getLogger(AsyncProcessor.class.getName());" << endl; indent(f_service_) << "public AsyncProcessor(I iface) {" << endl; indent(f_service_) << " super(iface, getProcessMap(new HashMap>()));" << endl; indent(f_service_) << "}" << endl << endl; indent(f_service_) << "protected AsyncProcessor(I iface, Map> processMap) {" << endl; indent(f_service_) << " super(iface, getProcessMap(processMap));" << endl; indent(f_service_) << "}" << endl << endl; indent(f_service_) << "private static Map> getProcessMap(Map> 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& fields = xs->get_members(); vector::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() << " extends org.apache.thrift.AsyncProcessFunction {" << 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 AsyncMethodCallback<"< getResultHandler(final AsyncFrameBuffer fb, final int seqid) {" << endl; indent_up(); indent(f_service_) << "final org.apache.thrift.AsyncProcessFunction fcall = this;"<() { " << endl; indent_up(); indent(f_service_) << "public void onComplete(" << resulttype <<" o) {" << endl; indent_up(); if (!tfunction->is_oneway()) { indent(f_service_) <get_returntype()->is_void()) { indent(f_service_) << "result.success = o;"<get_returntype())) { indent(f_service_) << "result.set" << get_cap_name("success") << get_cap_name("isSet") << "(true);" << endl; } } indent(f_service_) << "try {"<is_oneway()) { indent(f_service_) <<"byte msgType = org.apache.thrift.protocol.TMessageType.REPLY;"<get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; bool first = true; if (xceptions.size() > 0) { for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { first ? first = false : indent(f_service_) << "else "; indent(f_service_) << "if (e instanceof " << type_name((*x_iter)->get_type(), false, false)<<") {" << endl; indent(f_service_) << indent() << "result." << (*x_iter)->get_name() << " = (" << type_name((*x_iter)->get_type(), false, false) << ") e;" << endl; indent(f_service_) << indent() << "result.set" << get_cap_name((*x_iter)->get_name()) << get_cap_name("isSet") << "(true);" << endl; indent(f_service_) << indent() << "msg = result;"<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<"< resultHandler) throws TException {" << endl; indent_up(); // Generate the function call t_struct* arg_struct = tfunction->get_arglist(); const std::vector& fields = arg_struct->get_members(); vector::const_iterator f_iter; f_service_ << indent(); 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(); } 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() << " extends org.apache.thrift.ProcessFunction {" << 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_) << "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& xceptions = xs->get_members(); vector::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& fields = arg_struct->get_members(); vector::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())) { 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(ofstream& 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 (((t_base_type*)type)->is_binary()) { out << "readBinary();"; } else { out << "readString();"; } break; case t_base_type::TYPE_BOOL: out << "readBool();"; break; case t_base_type::TYPE_BYTE: 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_java_generator::generate_deserialize_struct(ofstream& 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_java_generator::generate_deserialize_container(ofstream& 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_set*)ttype)->get_elem_type()) << ", iprot.readI32());" << endl; } } indent(out) << prefix << " = new " << type_name(ttype, false, true); // size the collection correctly if (sorted_containers_ && (ttype->is_map() || ttype->is_set())) { // TreeSet and TreeMap don't have any constructor which takes a capactity as an argument out << "();" << endl; } else { out << "(" << (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, has_metadata); } else if (ttype->is_set()) { generate_deserialize_set_element(out, (t_set*)ttype, prefix, has_metadata); } else if (ttype->is_list()) { generate_deserialize_list_element(out, (t_list*)ttype, prefix, 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(ofstream& out, t_map* tmap, string prefix, 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) << endl; indent(out) << declare_field(&fval) << endl; generate_deserialize_field(out, &fkey, "", has_metadata); generate_deserialize_field(out, &fval, "", has_metadata); indent(out) << prefix << ".put(" << key << ", " << val << ");" << endl; } /** * Deserializes a set element */ void t_java_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix, bool has_metadata) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); indent(out) << declare_field(&felem) << endl; generate_deserialize_field(out, &felem, "", has_metadata); indent(out) << prefix << ".add(" << elem << ");" << endl; } /** * Deserializes a list element */ void t_java_generator::generate_deserialize_list_element(ofstream& out, t_list* tlist, string prefix, bool has_metadata) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); indent(out) << declare_field(&felem) << endl; generate_deserialize_field(out, &felem, "", has_metadata); 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_java_generator::generate_serialize_field(ofstream& 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 (((t_base_type*)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_BYTE: 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(ofstream& 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(ofstream& 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 (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(ofstream& 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(ofstream& 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(ofstream& 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. HashMap */ string t_java_generator::type_name(t_type* ttype, bool in_container, bool in_init, bool 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()) { t_map* tmap = (t_map*) ttype; if (in_init) { if (sorted_containers_) { prefix = "TreeMap"; } else { prefix = "HashMap"; } } else { prefix = "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 (sorted_containers_) { prefix = "TreeSet"; } else { prefix = "HashSet"; } } else { prefix = "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 = "ArrayList"; } else { prefix = "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_) { 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 "ByteBuffer"; } else { return "String"; } case t_base_type::TYPE_BOOL: return (in_container ? "Boolean" : "boolean"); case t_base_type::TYPE_BYTE: 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 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 = 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) { 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_BYTE: 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) + "()";; } } 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 result = type_name(ttype) + " " + prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ") throws "; t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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 result = prefix + "void " + tfunction->get_name() + "(" + arglist + ")"; return result; } string t_java_generator::async_function_call_arglist(t_function* tfunc, bool use_base_method, bool include_types) { 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 += "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& fields = tstruct->get_members(); vector::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) ttype; string result = ""; const vector& fields = tstruct->get_members(); vector::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 += "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_BYTE: 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; } /** * Applies the correct style to a string based on the value of nocamel_style_ */ std::string t_java_generator::get_cap_name(std::string name){ if (nocamel_style_) { return "_" + 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 (string::iterator iter = name.begin(); iter != name.end(); ++iter) { string::value_type character = (*iter); 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_java_docstring_comment(ofstream &out, string contents) { generate_docstring_comment(out, "/**\n", " * ", contents, " */\n"); } void t_java_generator::generate_java_doc(ofstream &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_java_generator::generate_java_doc(ofstream &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_java_generator::generate_java_doc(ofstream &out, t_function* tfunction) { if (tfunction->has_doc()) { stringstream ss; ss << tfunction->get_doc(); const vector& fields = tfunction->get_arglist()->get_members(); vector::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_java_generator::generate_deep_copy_container(ofstream &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 capacity; if (!(sorted_containers_ && (container->is_map() || container->is_set()))) { // unsorted containers accept a capacity value capacity = source_name + ".size()"; } indent(out) << type_name(type, true, false) << " " << result_name << " = new " << type_name(container, false, true) << "(" << capacity << ");" << 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 (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(((t_base_type*)elem_type)->is_binary()){ indent(out) << "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(ofstream& out, std::string source_name, std::string dest_name, t_type* type) { (void) dest_name; if (type->is_base_type() || type->is_enum() || type->is_typedef()) { if (((t_base_type*)type)->is_binary()) { out << "org.apache.thrift.TBaseHelper.copyBinary(" << source_name << ");" << endl; } 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(ofstream& 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; } } std::string t_java_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_java_generator::generate_struct_desc(ofstream& 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(ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { indent(out) << "private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>();" << endl; indent(out) << "static {" << endl; indent(out) << " schemes.put(StandardScheme.class, new " << tstruct->get_name() << "StandardSchemeFactory());" << endl; indent(out) << " schemes.put(TupleScheme.class, new " << tstruct->get_name() << "TupleSchemeFactory());" << endl; indent(out) << "}" << endl; } void t_java_generator::generate_field_name_constants(ofstream& 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& members = tstruct->get_members(); vector::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 Map byName = new HashMap();" << endl; out << endl; indent(out) << "static {" << endl; indent(out) << " for (_Fields field : 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) << "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 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) << "public static _Fields findByName(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 String _fieldName;" << endl << endl; indent(out) << "_Fields(short thriftId, 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 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& members = tstruct->get_members(); vector::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::ofstream& out, t_struct* tstruct) { if (!java5_) { indent(out) << "@Override" << endl; } indent(out) << "public void clear() {" << endl; const vector& members = tstruct->get_members(); vector::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)) { 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_BYTE: 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(ofstream& 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(ofstream& out, t_struct* tstruct) { indent(out) << "private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, 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 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(ofstream& 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& fields = tstruct->get_members(); vector::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(ofstream& 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& fields = tstruct->get_sorted_members(); vector::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(ofstream& out, t_struct* tstruct, bool is_result){ indent(out) << "private static class " << tstruct->get_name() << "StandardSchemeFactory implements 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 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(ofstream& 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) << "TTupleProtocol iprot = (TTupleProtocol) prot;" << endl; int optional_count = 0; const vector& fields = tstruct->get_members(); vector::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) << "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(ofstream& 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) << "TTupleProtocol oprot = (TTupleProtocol) prot;" << endl; const vector& fields = tstruct->get_members(); vector::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) << "BitSet optionals = new 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(ofstream& out, t_struct* tstruct){ indent(out) << "private static class " << tstruct->get_name() << "TupleSchemeFactory implements 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 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; } 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" " hashcode: Generate quality hashCode methods.\n" " android_legacy: Do not use java.io.IOException(throwable) (available for Android 2.3 and above).\n" " java5: Generate Java 1.5 compliant code (includes android_legacy flag).\n" " sorted_containers:\n" " Use TreeSet/TreeMap instead of HashSet/HashMap as a implementation of set/map.\n" ) thrift-compiler_0.9.1/cpp/src/generate/t_xsd_generator.cc0000644000175000017500000002636712203157755024365 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include "t_generator.h" #include "platform.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& parsed_options, const std::string& option_string) : t_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-xsd"; } virtual ~t_xsd_generator() {} /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef(t_typedef* ttypedef); void generate_enum(t_enum* tenum) { (void) tenum; } void generate_service(t_service* tservice); void generate_struct(t_struct* tstruct); 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); /** * Output xsd/php file */ std::ofstream f_xsd_; std::ofstream 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_ << "" << endl; f_php_.close(); } void t_xsd_generator::generate_typedef(t_typedef* ttypedef) { indent(s_xsd_types_) << "get_name() << "\">" << endl; indent_up(); if (ttypedef->get_type()->is_string() && ((t_base_type*)ttypedef->get_type())->is_string_enum()) { indent(s_xsd_types_) << "get_type()) << "\">" << endl; indent_up(); const vector& values = ((t_base_type*)ttypedef->get_type())->get_string_enum_vals(); vector::const_iterator v_iter; for (v_iter = values.begin(); v_iter != values.end(); ++v_iter) { indent(s_xsd_types_) << "" << endl; } indent_down(); indent(s_xsd_types_) << "" << endl; } else { indent(s_xsd_types_) << "get_type()) << "\" />" << endl; } indent_down(); indent(s_xsd_types_) << "" << endl << endl; } void t_xsd_generator::generate_struct(t_struct* tstruct) { vector::const_iterator m_iter; const vector& members = tstruct->get_members(); bool xsd_all = tstruct->get_xsd_all(); indent(s_xsd_types_) << "get_name() << "\">" << endl; indent_up(); if (xsd_all) { indent(s_xsd_types_) << "" << endl; } else { indent(s_xsd_types_) << "" << 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_) << "" << endl; } else { indent(s_xsd_types_) << "" << endl; } indent_down(); indent(s_xsd_types_) << "" << 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) << "" << endl; indent_up(); if (attrs == NULL && ttype->is_void()) { indent(out) << "" << endl; } else { indent(out) << "" << endl; indent_up(); if (ttype->is_list()) { indent(out) << "" << 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) << "" << endl; indent(out) << "" << endl; } if (attrs != NULL) { const vector& members = attrs->get_members(); vector::const_iterator a_iter; for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) { indent(out) << "get_name() << "\" type=\"" << type_name((*a_iter)->get_type()) << "\" />" << endl; } } indent_down(); indent(out) << "" << endl; } indent_down(); indent(out) << "" << endl; } else { if (attrs == NULL) { indent(out) << "" << endl; } else { // Wow, all this work for a SIMPLE TYPE with attributes?!?!?! indent(out) << "" << endl; indent_up(); indent(out) << "" << endl; indent_up(); indent(out) << "" << endl; indent_up(); indent(out) << "" << endl; indent_up(); const vector& members = attrs->get_members(); vector::const_iterator a_iter; for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) { indent(out) << "get_name() << "\" type=\"" << type_name((*a_iter)->get_type()) << "\" />" << endl; } indent_down(); indent(out) << "" << endl; indent_down(); indent(out) << "" << endl; indent_down(); indent(out) << "" << endl; indent_down(); indent(out) << "" << 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"); if (ns.size() > 0) { ns = " targetNamespace=\"" + ns + "\" xmlns=\"" + ns + "\" " + "elementFormDefault=\"qualified\""; } // Print the XSD header f_xsd_ << "" << endl << "" << endl << endl << "" << endl << 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 all_xceptions; // List the elements that you might actually get vector functions = tservice->get_functions(); vector::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& xceptions = xs->get_members(); vector::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::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 << "" << 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_BYTE: 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 C++ base type name for base type " + t_base_type::t_base_name(tbase); } } THRIFT_REGISTER_GENERATOR(xsd, "XSD", "") thrift-compiler_0.9.1/cpp/src/generate/t_d_generator.cc0000644000175000017500000006053212203157755024002 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "platform.h" #include "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& parsed_options, const string& option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-d"; } protected: virtual void init_generator() { // 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& includes = program_->get_includes(); for (size_t i = 0; i < includes.size(); ++i) { f_types_ << "import " << render_package(*(includes[i])) << includes[i]->get_name() << "_types;" << endl; } if (!includes.empty()) f_types_ << endl; } virtual void close_generator() { // Close output file f_types_.close(); } virtual void generate_consts(std::vector consts) { if (!consts.empty()) { string f_consts_name = package_dir_+program_name_+"_constants.d"; ofstream 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::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(); indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << endl; } f_consts << endl << "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) << (*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; } } virtual void generate_typedef(t_typedef* ttypedef) { f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " " << ttypedef->get_symbolic() << ";" << endl << endl; } virtual void generate_enum(t_enum* tenum) { vector constants = tenum->get_constants(); string enum_name = tenum->get_name(); f_types_ << indent() << "enum " << enum_name << " {" << endl; indent_up(); vector::const_iterator c_iter; bool first = true; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { if (first) { first = false; } else { f_types_ << "," << endl; } indent(f_types_) << (*c_iter)->get_name(); if ((*c_iter)->has_value()) { f_types_ << " = " << (*c_iter)->get_value(); } } f_types_ << endl; indent_down(); indent(f_types_) << "}" << endl; f_types_ << endl; } virtual void generate_struct(t_struct* tstruct) { print_struct_definition(f_types_, tstruct, false); } virtual void generate_xception(t_struct* txception) { print_struct_definition(f_types_, txception, true); } virtual void generate_service(t_service* tservice) { string svc_name = tservice->get_name(); // Service implementation file includes string f_servicename = package_dir_ + svc_name + ".d"; std::ofstream f_service; f_service.open(f_servicename.c_str()); f_service << autogen_comment() << "module " << render_package(*program_) << svc_name << ";" << endl << endl; print_default_imports(f_service); f_service << "import " << render_package(*get_program()) << program_name_ << "_types;" << endl; t_service* extends_service = tservice->get_extends(); if (extends_service != NULL) { f_service << "import " << render_package(*(extends_service->get_program())) << extends_service->get_name() << ";" << endl; } f_service << endl; string extends = ""; if (tservice->get_extends() != NULL) { extends = " : " + render_type_name(tservice->get_extends()); } 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 exception_types; // Print the method signatures. vector functions = tservice->get_functions(); vector::iterator fn_iter; for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { f_service << indent(); print_function_signature(f_service, *fn_iter); f_service << ";" << endl; const vector& exceptions = (*fn_iter)->get_xceptions()->get_members(); vector::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::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(`" << (*fn_iter)->get_name() << "`, " << endl; indent_up(); indent(meta) << "["; bool first = true; const vector ¶ms = (*fn_iter)->get_arglist()->get_members(); vector::const_iterator p_iter; for (p_iter = params.begin(); p_iter != params.end(); ++p_iter) { if (first) { first = false; } else { meta << ", "; } meta << "TParamMeta(`" << (*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& exceptions = (*fn_iter)->get_xceptions()->get_members(); vector::const_iterator ex_iter; for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { if (first) { first = false; } else { meta << ", "; } meta << "TExceptionMeta(`" << (*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"; std::ofstream f_skeleton; f_skeleton.open(f_skeletonname.c_str()); print_server_skeleton(f_skeleton, tservice); f_skeleton.close(); } private: /** * Writes a server skeleton for the passed service to out. */ void print_server_skeleton(ostream &out, t_service* tservice) { string svc_name = 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 functions = tservice->get_functions(); vector::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(\"" << (*f_iter)->get_name() << " called\");" << endl; t_base_type* rt = (t_base_type*)(*f_iter)->get_returntype(); if (rt->get_base() != t_base_type::TYPE_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& members = tstruct->get_members(); if (is_exception) { indent(out) << "class " << tstruct->get_name() << " : TException {" << endl; } else { indent(out) << "struct " << tstruct->get_name() << " {" << endl; } indent_up(); // Declare all fields. vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { indent(out) << render_type_name((*m_iter)->get_type()) << " " << (*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::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(`" << (*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()) << " " << fn->get_name() << "("; const vector& fields = fn->get_arglist()->get_members(); vector::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) << " " << (*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_BYTE: 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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& val = value->get_list(); vector::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_BYTE: 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: throw "Compiler error: Invalid requirement level: " + req; } } /** * 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 f_types_; ofstream f_header_; string package_dir_; }; THRIFT_REGISTER_GENERATOR(d, "D", "") thrift-compiler_0.9.1/cpp/src/generate/t_go_generator.cc0000644000175000017500000036325012203423600024150 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "t_generator.h" #include "platform.h" #include "version.h" using namespace std; /** * 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 = "git.apache.org/thrift.git/lib/go/thrift"; /** * Go code generator. */ class t_go_generator : public t_generator { public: t_go_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_generator(program) { std::map::const_iterator iter; out_dir_base_ = "gen-go"; gen_thrift_import_ = default_thrift_import; iter = parsed_options.find("package_prefix"); if (iter != parsed_options.end()) { gen_package_prefix_ = (iter->second); } iter = parsed_options.find("thrift_import"); if (iter != parsed_options.end()) { gen_thrift_import_ = (iter->second); } } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef(t_typedef* ttypedef); void generate_enum(t_enum* tenum); void generate_const(t_const* tconst); void generate_struct(t_struct* tstruct); void generate_xception(t_struct* txception); void generate_service(t_service* tservice); std::string render_const_value(t_type* type, t_const_value* value, const string& name); /** * Struct generation code */ void generate_go_struct(t_struct* tstruct, bool is_exception); void generate_go_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception = false, bool is_result = false); void generate_isset_helpers(std::ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result = false); void generate_go_struct_reader(std::ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result = false); void generate_go_struct_writer(std::ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result = false); void generate_go_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::ofstream &out, t_field* tfield, bool declare, std::string prefix = "", bool inclass = false, bool coerceData = false); void generate_deserialize_struct(std::ofstream &out, t_struct* tstruct, bool declare, std::string prefix = ""); void generate_deserialize_container(std::ofstream &out, t_type* ttype, bool declare, std::string prefix = ""); void generate_deserialize_set_element(std::ofstream &out, t_set* tset, bool declare, std::string prefix = ""); void generate_deserialize_map_element(std::ofstream &out, t_map* tmap, bool declare, std::string prefix = ""); void generate_deserialize_list_element(std::ofstream &out, t_list* tlist, bool declare, std::string prefix = ""); void generate_serialize_field(std::ofstream &out, t_field* tfield, std::string prefix = ""); void generate_serialize_struct(std::ofstream &out, t_struct* tstruct, std::string prefix = ""); void generate_serialize_container(std::ofstream &out, t_type* ttype, std::string prefix = ""); void generate_serialize_map_element(std::ofstream &out, t_map* tmap, std::string kiter, std::string viter); void generate_serialize_set_element(std::ofstream &out, t_set* tmap, std::string iter); void generate_serialize_list_element(std::ofstream &out, t_list* tlist, std::string iter); void generate_go_docstring(std::ofstream& out, t_struct* tstruct); void generate_go_docstring(std::ofstream& out, t_function* tfunction); void generate_go_docstring(std::ofstream& out, t_doc* tdoc, t_struct* tstruct, const char* subheader); void generate_go_docstring(std::ofstream& out, t_doc* tdoc); /** * Helper rendering functions */ std::string go_autogen_comment(); std::string go_package(); std::string go_imports_begin(); std::string go_imports_end(); std::string render_includes(); std::string render_import_protection(); std::string render_fastbinary_includes(); std::string declare_argument(t_field* tfield); std::string render_field_default_value(t_field* tfield, const string& name); std::string type_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_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) { std::string real_module = program->get_namespace("go"); if (real_module.empty()) { return program->get_name(); } return real_module; } private: std::string gen_package_prefix_; std::string gen_thrift_import_; /** * File streams */ std::ofstream f_types_; std::string f_types_name_; std::ofstream f_consts_; std::string f_consts_name_; std::stringstream f_const_values_; std::ofstream f_service_; std::string package_name_; std::string package_dir_; static std::string publicize(const std::string& value); static std::string new_prefix(const std::string& value); static std::string privatize(const std::string& value); static std::string variable_name_to_go_name(const std::string& value); static bool can_be_nil(t_type* value); }; std::string t_go_generator::publicize(const std::string& value) { 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]); } // as long as we are changing things, let's change _ followed by lowercase to capital for (string::size_type i = 1; i < value2.size() - 1; ++i) { if (value2[i] == '_' && islower(value2[i + 1])) { value2.replace(i, 2, 1, toupper(value2[i + 1])); } } return prefix + value2; } std::string t_go_generator::new_prefix(const std::string& value) { 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) { if (value.size() <= 0) { return value; } std::string value2(value); if (!islower(value2[0])) { value2[0] = tolower(value2[0]); } // as long as we are changing things, let's change _ followed by lowercase to capital for (string::size_type i = 1; i < value2.size() - 1; ++i) { if (value2[i] == '_' && isalpha(value2[i + 1])) { value2.replace(i, 2, 1, toupper(value2[i + 1])); } } 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(); 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_ + "/" + "ttypes.go"; f_types_.open(f_types_name_.c_str()); f_consts_name_ = package_dir_ + "/" + "constants.go"; f_consts_.open(f_consts_name_.c_str()); vector services = program_->get_services(); vector::iterator sv_iter; for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { string service_dir = package_dir_ + "/" + underscore((*sv_iter)->get_name()) + "-remote"; MKDIR(service_dir.c_str()); } // Print header f_types_ << go_autogen_comment() << go_package() << render_includes() << render_import_protection(); f_consts_ << go_autogen_comment() << go_package() << render_includes(); f_const_values_ << endl << "func init() {" << endl; } /** * Renders all the imports necessary for including another Thrift program */ string t_go_generator::render_includes() { const vector& includes = program_->get_includes(); string result = ""; string unused_prot = ""; for (size_t i = 0; i < includes.size(); ++i) { string go_module = get_real_go_module(includes[i]); 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_module[j] = '/'; found = j + 1; } } result += "\t\"" + gen_package_prefix_ + go_module + "\"\n"; unused_prot += "var _ = " + go_module.substr(found) + ".GoUnusedProtection__\n"; } if (includes.size() > 0) { result += "\n"; } return go_imports_begin() + 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 */ string t_go_generator::go_imports_begin() { return string("import (\n" "\t\"fmt\"\n" "\t\"math\"\n" "\t\"" + gen_thrift_import_ + "\"\n"); } /** * End the import statement, include undscore-assignments * * These "_ =" prevent the go compiler complaining about used 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 _ = math.MinInt32\n" "var _ = thrift.ZERO\n" "var _ = fmt.Printf\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. This is not done in go, types are all implicit. * * @param ttypedef The type definition */ void t_go_generator::generate_typedef(t_typedef* ttypedef) { generate_go_docstring(f_types_, ttypedef); string newTypeDef(publicize(ttypedef->get_symbolic())); string baseType(type_to_go_type(ttypedef->get_type())); if (baseType == newTypeDef) { return; } f_types_ << "type " << newTypeDef << " " << baseType << 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 << indent() << " switch p {" << endl; from_string_mapping << indent() << "func " << tenum_name << "FromString(s string) (" << tenum_name << ", error) {" << endl << indent() << " switch s {" << endl; vector constants = tenum->get_constants(); vector::iterator c_iter; int value = -1; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { if ((*c_iter)->has_value()) { value = (*c_iter)->get_value(); } else { ++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 \"" << tenum_name << "_" << iter_std_name << "\"" << endl; if (iter_std_name != escape_string(iter_name)) { from_string_mapping << indent() << " case \"" << tenum_name << "_" << iter_std_name << "\", \"" << escape_string(iter_name) << "\": return " << tenum_name << "_" << iter_name << ", nil " << endl; } else { from_string_mapping << indent() << " case \"" << tenum_name << "_" << iter_std_name << "\": return " << tenum_name << "_" << iter_name << ", nil " << endl; } } to_string_mapping << indent() << " }" << endl << indent() << " return \"\"" << endl << indent() << "}" << endl; from_string_mapping << indent() << " }" << endl << indent() << " return " << tenum_name << "(math.MinInt32 - 1)," << " fmt.Errorf(\"not a valid " << tenum_name << " string\")" << endl << indent() << "}" << endl; f_types_ << ")" << endl << endl << to_string_mapping.str() << endl << from_string_mapping.str() << endl << 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) { 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 << "[]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_BYTE: 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 << "&" << publicize(type_name(type)) << "{"; indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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(); } if (field_type->is_base_type() || field_type->is_enum()) { out << endl << indent() << publicize(v_iter->first->get_string()) << ": " << render_const_value(field_type, v_iter->second, name) << ","; } else { string k(tmp("k")); string v(tmp("v")); out << endl << indent() << v << " := " << render_const_value(field_type, v_iter->second, v) << endl << indent() << name << "." << publicize(v_iter->first->get_string()) << " = " << v; } } out << "}"; 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& val = value->get_map(); out << "map[" << type_to_go_type(ktype) << "]" << type_to_go_type(vtype) << "{" << endl; indent_up(); map::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& val = value->get_list(); out << "[]" << type_to_go_type(etype) << "{" << endl; indent_up(); vector::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& val = value->get_list(); out << "map[" << type_to_go_key_type(etype) << "]bool{" << endl; indent_up(); vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { out << indent() << render_const_value(etype, *v_iter, name) << ": true," << endl; } 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); } /** * Generates a struct definition for a thrift data type. * * @param tstruct The struct definition */ void t_go_generator::generate_go_struct_definition(ofstream& out, t_struct* tstruct, bool is_exception, bool is_result) { const vector& members = tstruct->get_members(); const vector& sorted_members = tstruct->get_sorted_members(); vector::const_iterator m_iter; std::string tstruct_name(publicize(tstruct->get_name())); 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(); 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) { for (; sorted_keys_pos != (*m_iter)->get_key(); sorted_keys_pos++) { if (sorted_keys_pos != 0) { indent(out) << "// unused field # " << sorted_keys_pos << endl; } } t_type* fieldType = (*m_iter)->get_type(); string goType(type_to_go_type(fieldType)); indent(out) << publicize(variable_name_to_go_name((*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 << "\"`" << 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 << indent() << "func New" << tstruct_name << "() *" << tstruct_name << " {" << endl << indent() << " return &" << tstruct_name << "{"; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { // Initialize fields const string base_field_name = (*m_iter)->get_name(); const string escaped_field_name = escape_string(base_field_name); const string go_safe_name = variable_name_to_go_name(escaped_field_name); const string publicized_name = publicize(go_safe_name); const t_type* type = get_true_type((*m_iter)->get_type()); const bool has_default_value = (*m_iter)->get_value() != NULL; const bool type_is_enum = type->is_enum(); if (has_default_value) { out << endl << indent() << publicized_name << ": " << render_field_default_value(*m_iter, base_field_name) << "," << endl; } else if (type_is_enum) { out << endl << indent() << publicized_name << ": math.MinInt32 - 1, // unset sentinal value" << endl; } } out << "}" << endl; out << "}" << endl << endl; 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); out << indent() << "func (p *" << tstruct_name << ") String() string {" << endl << indent() << " if p == nil {" << endl << indent() << " return \"\"" << endl << indent() << " }" << endl << indent() << " return fmt.Sprintf(\"" << escape_string(tstruct_name) << "(%+v)\", *p)" << endl << indent() << "}" << endl << endl; } /** * Generates the IsSet helper methods for a struct */ void t_go_generator::generate_isset_helpers(ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result) { const vector& fields = tstruct->get_members(); vector::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) { t_type* type = get_true_type((*f_iter)->get_type()); if ((*f_iter)->get_req() == t_field::T_OPTIONAL || type->is_enum()) { const string field_name(publicize(variable_name_to_go_name(escape_string((*f_iter)->get_name())))); t_const_value* field_default_value = (*f_iter)->get_value(); out << indent() << "func (p *" << tstruct_name << ") IsSet" << field_name << "() bool {" << endl; indent_up(); string s_check_value; int64_t i_check_value; double d_check_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_STRING: if (((t_base_type*)type)->is_binary()) { // ignore default value for binary out << indent() << "return p." << field_name << " != nil" << endl; } else { s_check_value = (field_default_value == NULL) ? "\"\"" : render_const_value(type, field_default_value, tstruct_name); out << indent() << "return p." << field_name << " != " << s_check_value << endl; } break; case t_base_type::TYPE_BOOL: s_check_value = (field_default_value != NULL && field_default_value->get_integer() > 0) ? "true" : "false"; out << indent() << "return p." << field_name << " != " << s_check_value << endl; break; case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: i_check_value = (field_default_value == NULL) ? 0 : field_default_value->get_integer(); out << indent() << "return p." << field_name << " != " << i_check_value << endl; break; case t_base_type::TYPE_DOUBLE: d_check_value = (field_default_value == NULL) ? 0.0 : field_default_value->get_double(); out << indent() << "return p." << field_name << " != " << d_check_value << endl; break; default: throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { out << indent() << "return int64(p." << field_name << ") != " << "math.MinInt32 - 1" << endl; } else if (type->is_struct() || type->is_xception()) { out << indent() << "return p." << field_name << " != nil" << endl; } else if (type->is_list() || type->is_set()) { if (field_default_value != NULL && field_default_value->get_list().size() > 0) { out << indent() << "return p." << field_name << " != nil" << endl; } else { out << indent() << "return p." << field_name << " != nil && len(p." << field_name << ") > 0" << endl; } } else if (type->is_map()) { if (field_default_value != NULL && field_default_value->get_map().size() > 0) { out << indent() << "return p." << field_name << " != nil" << endl; } else { out << indent() << "return p." << field_name << " != nil && len(p." << field_name << ") > 0" << endl; } } else { throw "CANNOT GENERATE ISSET HELPERS FOR TYPE: " + type->get_name(); } indent_down(); out << indent() << "}" << endl << endl; } } } /** * Generates the read method for a struct */ void t_go_generator::generate_go_struct_reader(ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; string escaped_tstruct_name(escape_string(tstruct->get_name())); out << indent() << "func (p *" << tstruct_name << ") Read(iprot thrift.TProtocol) error {" << endl; indent_up(); out << indent() << "if _, err := iprot.ReadStructBegin(); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T read error\", p)" << endl << indent() << "}" << endl; // Loop over reading in fields indent(out) << "for {" << endl; indent_up(); // Read beginning field marker out << indent() << "_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()" << endl << indent() << "if err != nil {" << endl << indent() << " return fmt.Errorf(\"%T field %d read error: %s\", p, fieldId, err)" << endl << indent() << "}" << endl; // Check for field STOP marker and break out << indent() << "if fieldTypeId == thrift.STOP { break; }" << endl; // Switch statement on the field we are reading bool first = true; string thriftFieldTypeId; // Generate deserialization code for known cases int32_t field_id = -1; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { field_id = (*f_iter)->get_key(); if (first) { first = false; indent(out) << "switch fieldId {" << endl; } // if negative id, ensure we generate a valid method name string field_method_prefix("readField"); if (field_id < 0) { field_method_prefix += "_"; field_id *= -1; } indent_up(); out << "case " << field_id << ":" << endl; thriftFieldTypeId = type_to_enum((*f_iter)->get_type()); if (thriftFieldTypeId == "thrift.BINARY") { thriftFieldTypeId = "thrift.STRING"; } out << indent() << "if err := p." << field_method_prefix << field_id << "(iprot); err != nil {" << endl << indent() << " return err" << endl << indent() << "}" << endl; indent_down(); } // In the default case we skip the field if (!first) { out << indent() << "default:" << endl << indent() << " if err := iprot.Skip(fieldTypeId); err != nil {" << endl << indent() << " return err" << endl << indent() << " }" << endl << indent() << "}" << endl; } // Read field end marker out << indent() << "if err := iprot.ReadFieldEnd(); err != nil {" << endl << indent() << " return err" << endl << indent() << "}" << endl; indent_down(); out << indent() << "}" << endl << indent() << "if err := iprot.ReadStructEnd(); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T read struct end error: %s\", p, err)" << endl << indent() << "}" << endl << 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(); if (field_id < 0) { field_method_prefix += "_"; field_id *= -1; } out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_id << "(iprot thrift.TProtocol) error {" << endl; indent_up(); generate_deserialize_field(out, *f_iter, false, "p."); indent_down(); out << indent() << " return nil" << endl << indent() << "}" << endl << endl; } } void t_go_generator::generate_go_struct_writer(ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result) { string name(tstruct->get_name()); const vector& fields = tstruct->get_sorted_members(); vector::const_iterator f_iter; indent(out) << "func (p *" << tstruct_name << ") Write(oprot thrift.TProtocol) error {" << endl; indent_up(); out << indent() << "if err := oprot.WriteStructBegin(\"" << name << "\"); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T write struct begin error: %s\", p, err) }" << endl; string field_name; string escape_field_name; //t_const_value* field_default_value; t_field::e_req field_required; bool field_can_be_nil = false; int32_t field_id = -1; if (is_result && fields.size()) { out << indent() << "switch {" << endl; vector::const_reverse_iterator fr_iter; for (fr_iter = fields.rbegin(); fr_iter != fields.rend(); ++fr_iter) { string field_method_prefix("writeField"); field_name = (*fr_iter)->get_name(); field_id = (*fr_iter)->get_key(); if (field_id < 0) { field_method_prefix += "_"; field_id *= -1; } if (can_be_nil((*fr_iter)->get_type()) && field_id != 0) { out << indent() << "case p." << publicize(variable_name_to_go_name(field_name)) << " != nil:" << endl << indent() << " if err := p." << field_method_prefix << field_id << "(oprot); err != nil { return err }" << endl; } else { out << indent() << "default:" << endl << indent() << " if err := p." << field_method_prefix << field_id << "(oprot); err != nil { return err }" << endl; } } out << indent() << "}" << endl; } else { 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(); if (field_id < 0) { field_method_prefix += "_"; field_id *= -1; } out << indent() << "if err := p." << field_method_prefix << field_id << "(oprot); err != nil { return err }" << endl; } } // Write the struct map out << indent() << "if err := oprot.WriteFieldStop(); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T write field stop error: %s\", err) }" << endl << indent() << "if err := oprot.WriteStructEnd(); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T write struct stop error: %s\", err) }" << endl << 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(); field_can_be_nil = can_be_nil((*f_iter)->get_type()); if (field_id < 0) { field_method_prefix += "_"; field_id *= -1; } out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_id << "(oprot thrift.TProtocol) (err error) {" << endl; indent_up(); // Write field header if (field_can_be_nil) { out << indent() << "if p." << publicize(variable_name_to_go_name(field_name)) << " != nil {" << endl; indent_up(); } if (field_required == t_field::T_OPTIONAL || (*f_iter)->get_type()->is_enum()) { out << indent() << "if p.IsSet" << publicize(variable_name_to_go_name(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 << indent() << " return fmt.Errorf(\"%T write field begin error " << field_id << ":" << escape_field_name << ": %s\", 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 << indent() << " return fmt.Errorf(\"%T write field end error " << field_id << ":" << escape_field_name << ": %s\", p, err); }" << endl; if (field_required == t_field::T_OPTIONAL || (*f_iter)->get_type()->is_enum()) { indent_down(); out << indent() << "}" << endl; } if (field_can_be_nil) { indent_down(); out << indent() << "}" << endl; } indent_down(); out << indent() << " return err" << endl << indent() << "}" << endl << endl; } } /** * Generates a thrift service. * * @param tservice The service definition */ void t_go_generator::generate_service(t_service* tservice) { string f_service_name = package_dir_ + "/" + underscore(service_name_) + ".go"; f_service_.open(f_service_name.c_str()); f_service_ << go_autogen_comment() << go_package() << render_includes(); 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_ << endl; f_service_.close(); format_go_output(f_service_name); } /** * 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 functions = tservice->get_functions(); vector::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(); generate_go_struct_definition(f_service_, ts, false); 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 (true || !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& fields = xs->get_members(); vector::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } generate_go_struct_definition(f_service_, &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_service_ << indent() << "type " << interfaceName << " interface {" << extends_if; indent_up(); generate_go_docstring(f_service_, tservice); vector functions = tservice->get_functions(); if (!functions.empty()) { f_service_ << endl; vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_go_docstring(f_service_, (*f_iter)); f_service_ << indent() << function_signature_if(*f_iter, "", true) << endl; } } indent_down(); f_service_ << 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_service_, tservice); f_service_ << indent() << "type " << serviceName << "Client struct {" << endl; indent_up(); if (!extends_client.empty()) { f_service_ << indent() << "*" << extends_client << endl; } else { f_service_ << indent() << "Transport thrift.TTransport" << endl << indent() << "ProtocolFactory thrift.TProtocolFactory" << endl << indent() << "InputProtocol thrift.TProtocol" << endl << indent() << "OutputProtocol thrift.TProtocol" << endl << indent() << "SeqId int32" << endl /*<< indent() << "reqs map[int32]Deferred" << endl*/; } indent_down(); f_service_ << indent() << "}" << endl << endl; // Constructor function f_service_ << indent() << "func New" << serviceName << "ClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *" << serviceName << "Client {" << endl; indent_up(); f_service_ << indent() << "return &" << serviceName << "Client"; if (!extends.empty()) { f_service_ << "{" << extends_field << ": " << extends_client_new << "Factory(t, f)}"; } else { indent_up(); f_service_ << "{Transport: t," << endl << indent() << "ProtocolFactory: f," << endl << indent() << "InputProtocol: f.GetProtocol(t)," << endl << indent() << "OutputProtocol: f.GetProtocol(t)," << endl << indent() << "SeqId: 0," << endl /*<< indent() << "Reqs: make(map[int32]Deferred)" << endl*/; indent_down(); f_service_ << indent() << "}" << endl; } indent_down(); f_service_ << indent() << "}" << endl << endl; // Constructor function f_service_ << indent() << "func New" << serviceName << "ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *" << serviceName << "Client {" << endl; indent_up(); f_service_ << indent() << "return &" << serviceName << "Client"; if (!extends.empty()) { f_service_ << "{" << extends_field << ": " << extends_client_new << "Protocol(t, iprot, oprot)}" << endl; } else { indent_up(); f_service_ << "{Transport: t," << endl << indent() << "ProtocolFactory: nil," << endl << indent() << "InputProtocol: iprot," << endl << indent() << "OutputProtocol: oprot," << endl << indent() << "SeqId: 0," << endl /*<< indent() << "Reqs: make(map[int32]interface{})" << endl*/; indent_down(); f_service_ << indent() << "}" << endl; } indent_down(); f_service_ << indent() << "}" << endl << endl; // Generate client method implementations vector functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::const_iterator fld_iter; string funname = publicize((*f_iter)->get_name()); // Open function generate_go_docstring(f_service_, (*f_iter)); f_service_ << indent() << "func (p *" << serviceName << "Client) " << function_signature_if(*f_iter, "", true) << " {" << endl; indent_up(); /* f_service_ << indent() << "p.SeqId += 1" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent() << "d := defer.Deferred()" << endl << indent() << "p.Reqs[p.SeqId] = d" << endl; } */ f_service_ << indent() << "if err = p.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_ << variable_name_to_go_name((*fld_iter)->get_name()); } f_service_ << "); err != nil { return }" << endl; if (!(*f_iter)->is_oneway()) { f_service_ << indent() << "return p.recv" << funname << "()" << endl; } else { f_service_ << indent() << "return" << endl; } indent_down(); f_service_ << indent() << "}" << endl << endl << indent() << "func (p *" << serviceName << "Client) send" << function_signature(*f_iter) << "(err error) {" << endl; indent_up(); std::string argsname = publicize((*f_iter)->get_name()) + "Args"; // Serialize the request header string args(tmp("args")); f_service_ << indent() << "oprot := p.OutputProtocol" << endl << indent() << "if oprot == nil {" << endl << indent() << " oprot = p.ProtocolFactory.GetProtocol(p.Transport)" << endl << indent() << " p.OutputProtocol = oprot" << endl << indent() << "}" << endl << indent() << "p.SeqId++" << endl << indent() << "oprot.WriteMessageBegin(\"" << (*f_iter)->get_name() << "\", thrift.CALL, p.SeqId)" << endl << indent() << args << " := New" << publicize(argsname) << "()" << endl; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << indent() << args << "." << publicize(variable_name_to_go_name((*fld_iter)->get_name())) << " = " << variable_name_to_go_name((*fld_iter)->get_name()) << endl; } // Write to the stream f_service_ << indent() << "err = " << args << ".Write(oprot)" << endl << indent() << "oprot.WriteMessageEnd()" << endl << indent() << "oprot.Flush()" << endl << indent() << "return" << endl; indent_down(); f_service_ << indent() << "}" << endl << endl; if (true) { //!(*f_iter)->is_oneway() || true) {} std::string resultname = publicize((*f_iter)->get_name()) + "Result"; // Open function f_service_ << endl << indent() << "func (p *" << serviceName << "Client) recv" << publicize((*f_iter)->get_name()) << "() ("; if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "value " << type_to_go_type((*f_iter)->get_returntype()) << ", "; } t_struct* exceptions = (*f_iter)->get_xceptions(); string errs = argument_list(exceptions); if (errs.size()) { f_service_ << errs << ", "; } f_service_ << "err error) {" << endl; indent_up(); // TODO(mcslee): Validate message reply here, seq ids etc. string result(tmp("result")); string error(tmp("error")); string error2(tmp("error")); f_service_ << indent() << "iprot := p.InputProtocol" << endl << indent() << "if iprot == nil {" << endl << indent() << " iprot = p.ProtocolFactory.GetProtocol(p.Transport)" << endl << indent() << " p.InputProtocol = iprot" << endl << indent() << "}" << endl << indent() << "_, mTypeId, seqId, err := iprot.ReadMessageBegin()" << endl << indent() << "if err != nil {" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "if mTypeId == thrift.EXCEPTION {" << endl << indent() << " " << error << " := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, \"Unknown Exception\")" << endl << indent() << " var " << error2 << " error" << endl << indent() << " " << error2 << ", err = " << error << ".Read(iprot)" << endl << indent() << " if err != nil {" << endl << indent() << " return" << endl << indent() << " }" << endl << indent() << " if err = iprot.ReadMessageEnd(); err != nil {" << endl << indent() << " return" << endl << indent() << " }" << endl << indent() << " err = " << error2 << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "if p.SeqId != seqId {" << endl << indent() << " err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"ping failed: out of sequence response\")" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << result << " := New" << publicize(resultname) << "()" << endl << indent() << "err = " << 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() << "value = " << result << ".Success" << endl; } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << indent() << "if " << result << "." << publicize((*x_iter)->get_name()) << " != nil {" << endl << indent() << " " << (*x_iter)->get_name() << " = " << result << "." << publicize((*x_iter)->get_name()) << endl << indent() << "}" << endl; } f_service_ << indent() << "return" << endl; // Close function indent_down(); f_service_ << indent() << "}" << endl << endl; } } //indent_down(); f_service_ << 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 functions = tservice->get_functions(); t_service* parent = tservice->get_extends(); // collect inherited functions while (parent != NULL) { vector p_functions = parent->get_functions(); functions.insert(functions.end(), p_functions.begin(), p_functions.end()); parent = parent->get_extends(); } vector::iterator f_iter; string f_remote_name = package_dir_ + "/" + underscore(service_name_) + "-remote/" + underscore(service_name_) + "-remote.go"; ofstream f_remote; f_remote.open(f_remote_name.c_str()); string service_module = get_real_go_module(program_); string::size_type loc; while ((loc = service_module.find(".")) != string::npos) { service_module.replace(loc, 1, 1, '/'); } f_remote << go_autogen_comment() << indent() << "package main" << endl << endl << indent() << "import (" << endl << indent() << " \"flag\"" << endl << indent() << " \"fmt\"" << endl << indent() << " \"math\"" << endl << indent() << " \"net\"" << endl << indent() << " \"net/url\"" << endl << indent() << " \"os\"" << endl << indent() << " \"strconv\"" << endl << indent() << " \"strings\"" << endl << indent() << " \"" + gen_thrift_import_ + "\"" << endl << indent() << " \"" << service_module << "\"" << endl << indent() << ")" << endl << indent() << endl << indent() << "func Usage() {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Usage of \", os.Args[0], \" [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:\")" << endl << indent() << " flag.PrintDefaults()" << endl << indent() << " fmt.Fprintln(os.Stderr, \"\\nFunctions:\")" << endl; 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& args = arg_struct->get_members(); vector::const_iterator a_iter; int num_args = args.size(); bool first = true; for (int 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 << indent() << " os.Exit(0)" << endl << indent() << "}" << endl << indent() << endl << indent() << "func main() {" << endl; indent_up(); f_remote << indent() << "flag.Usage = Usage" << endl << indent() << "var host string" << endl << indent() << "var port int" << endl << indent() << "var protocol string" << endl << indent() << "var urlString string" << endl << indent() << "var framed bool" << endl << indent() << "var useHttp bool" << endl << indent() << "var parsedUrl url.URL" << endl << indent() << "var trans thrift.TTransport" << endl << indent() << "_ = math.MinInt32 // will become unneeded eventually" << endl << indent() << "_ = strconv.Atoi" << endl << indent() << "flag.Usage = Usage" << endl << indent() << "flag.StringVar(&host, \"h\", \"localhost\", \"Specify host and port\")" << endl << indent() << "flag.IntVar(&port, \"p\", 9090, \"Specify port\")" << endl << indent() << "flag.StringVar(&protocol, \"P\", \"binary\", \"Specify the protocol (binary, compact, simplejson, json)\")" << endl << indent() << "flag.StringVar(&urlString, \"u\", \"\", \"Specify the url\")" << endl << indent() << "flag.BoolVar(&framed, \"framed\", false, \"Use framed transport\")" << endl << indent() << "flag.BoolVar(&useHttp, \"http\", false, \"Use http\")" << endl << indent() << "flag.Parse()" << endl << indent() << endl << indent() << "if len(urlString) > 0 {" << endl << indent() << " parsedUrl, err := url.Parse(urlString)" << endl << indent() << " if err != nil {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << endl << indent() << " flag.Usage()" << endl << indent() << " }" << endl << indent() << " host = parsedUrl.Host" << endl << indent() << " useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == \"http\"" << endl << indent() << "} else if useHttp {" << endl << indent() << " _, err := url.Parse(fmt.Sprint(\"http://\", host, \":\", port))" << endl << indent() << " if err != nil {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << endl << indent() << " flag.Usage()" << endl << indent() << " }" << endl << indent() << "}" << endl << indent() << endl << indent() << "cmd := flag.Arg(0)" << endl << indent() << "var err error" << endl << indent() << "if useHttp {" << endl << indent() << " trans, err = thrift.NewTHttpClient(parsedUrl.String())" << endl << indent() << "} else {" << endl << indent() << " portStr := fmt.Sprint(port)" << endl << indent() << " if strings.Contains(host, \":\") {" << endl << indent() << " host, portStr, err = net.SplitHostPort(host)" << endl << indent() << " if err != nil {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"error with host:\", err)" << endl << indent() << " os.Exit(1)" << endl << indent() << " }" << endl << indent() << " }" << endl << indent() << " trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr))" << endl << indent() << " if err != nil {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"error resolving address:\", err)" << endl << indent() << " os.Exit(1)" << endl << indent() << " }" << endl << indent() << " if framed {" << endl << indent() << " trans = thrift.NewTFramedTransport(trans)" << endl << indent() << " }" << endl << indent() << "}" << endl << indent() << "if err != nil {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Error creating transport\", err)" << endl << indent() << " os.Exit(1)" << endl << indent() << "}" << endl << indent() << "defer trans.Close()" << endl << indent() << "var protocolFactory thrift.TProtocolFactory" << endl << indent() << "switch protocol {" << endl << indent() << "case \"compact\":" << endl << indent() << " protocolFactory = thrift.NewTCompactProtocolFactory()" << endl << indent() << " break" << endl << indent() << "case \"simplejson\":" << endl << indent() << " protocolFactory = thrift.NewTSimpleJSONProtocolFactory()" << endl << indent() << " break" << endl << indent() << "case \"json\":" << endl << indent() << " protocolFactory = thrift.NewTJSONProtocolFactory()" << endl << indent() << " break" << endl << indent() << "case \"binary\", \"\":" << endl << indent() << " protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()" << endl << indent() << " break" << endl << indent() << "default:" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Invalid protocol specified: \", protocol)" << endl << indent() << " Usage()" << endl << indent() << " os.Exit(1)" << endl << indent() << "}" << endl << indent() << "client := " << package_name_ << ".New" << publicize(service_name_) << "ClientFactory(trans, protocolFactory)" << endl << indent() << "if err := trans.Open(); err != nil {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Error opening socket to \", host, \":\", port, \" \", err)" << endl << indent() << " os.Exit(1)" << endl << indent() << "}" << endl << indent() << endl << 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& args = arg_struct->get_members(); vector::const_iterator a_iter; int num_args = args.size(); string funcName((*f_iter)->get_name()); string pubName(publicize(funcName)); f_remote << indent() << "case \"" << escape_string(funcName) << "\":" << endl; indent_up(); f_remote << indent() << "if flag.NArg() - 1 != " << num_args << " {" << endl << indent() << " fmt.Fprintln(os.Stderr, \"" << escape_string(pubName) << " requires " << num_args << " args\")" << endl << indent() << " flag.Usage()" << endl << indent() << "}" << endl; for (int i = 0; i < num_args; ++i) { int 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 << indent() << "if err != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "argvalue" << i << " := " << package_name_ << "." << 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: 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_BYTE: f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << indent() << "if " << err << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "argvalue" << i << " := byte(tmp" << i << ")" << endl; break; case t_base_type::TYPE_I16: f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << indent() << "if " << err << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "argvalue" << i << " := byte(tmp" << i << ")" << endl; break; case t_base_type::TYPE_I32: f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << indent() << "if " << err << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << 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 << indent() << "if " << err << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl; break; case t_base_type::TYPE_DOUBLE: f_remote << indent() << "argvalue" << i << ", " << err << " := (strconv.ParseFloat(flag.Arg(" << flagArg << "), 64))" << endl << indent() << "if " << err << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << 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())); f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << endl << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))" << endl << indent() << "defer " << mbTrans << ".Close()" << endl << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")" << endl << indent() << "if " << err1 << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << factory << " := thrift.NewTSimpleJSONProtocolFactory()" << endl << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" << endl << indent() << "argvalue" << i << " := " << package_name_ << ".New" << tstruct_name << "()" << endl << indent() << err2 << " := argvalue" << i << ".Read(" << jsProt << ")" << endl << indent() << "if " << err2 << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << 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 << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))" << endl << indent() << "defer " << mbTrans << ".Close()" << endl << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")" << endl << indent() << "if " << err1 << " != nil { " << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << factory << " := thrift.NewTSimpleJSONProtocolFactory()" << endl << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" << endl << indent() << "containerStruct" << i << " := " << package_name_ << ".New" << pubName << "Args()" << endl << indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "(" << jsProt << ")" << endl << indent() << "if " << err2 << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "argvalue" << i << " := containerStruct" << i << "." << argName << endl; } else { throw ("Invalid argument type in generate_service_remote"); string err1(tmp("err")); f_remote << indent() << "argvalue" << i << ", " << err1 << " := eval(flag.Arg(" << flagArg << "))" << endl << indent() << "if " << err1 << " != nil {" << endl << indent() << " Usage()" << endl << indent() << " return" << endl << indent() << "}" << endl; } if (the_type->is_typedef()) { f_remote << indent() << "value" << i << " := " << package_name_ << "." << 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; for (int i = 0; i < num_args; ++i) { if (argFirst) { argFirst = false; } 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_BYTE: 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 << indent() << "fmt.Print(\"\\n\")" << endl << indent() << "break" << endl; indent_down(); } f_remote << indent() << "case \"\":" << endl << indent() << " Usage()" << endl << indent() << " break" << endl << indent() << "default:" << endl << indent() << " fmt.Fprintln(os.Stderr, \"Invalid function \", cmd)" << endl << 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 MINGW | 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 functions = tservice->get_functions(); vector::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(serviceName)); // Generate the header portion string self(tmp("self")); if (extends_processor.empty()) { f_service_ << indent() << "type " << serviceName << "Processor struct {" << endl << indent() << " processorMap map[string]thrift.TProcessorFunction" << endl << indent() << " handler " << serviceName << endl << indent() << "}" << endl << endl << indent() << "func (p *" << serviceName << "Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {" << endl << indent() << " p.processorMap[key] = processor" << endl << indent() << "}" << endl << endl << indent() << "func (p *" << serviceName << "Processor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {" << endl << indent() << " processor, ok = p.processorMap[key]" << endl << indent() << " return processor, ok" << endl << indent() << "}" << endl << endl << indent() << "func (p *" << serviceName << "Processor) ProcessorMap() map[string]thrift.TProcessorFunction {" << endl << indent() << " return p.processorMap" << endl << indent() << "}" << endl << endl << indent() << "func New" << serviceName << "Processor(handler " << serviceName << ") *" << serviceName << "Processor {" << endl << endl << 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_service_ << indent() << " " << self << ".processorMap[\"" << escapedFuncName << "\"] = &" << pServiceName << "Processor" << publicize((*f_iter)->get_name()) << "{handler:handler}" << endl; } string x(tmp("x")); f_service_ << indent() << "return " << self << endl << indent() << "}" << endl << endl << indent() << "func (p *" << serviceName << "Processor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {" << endl << indent() << " name, _, seqId, err := iprot.ReadMessageBegin()" << endl << indent() << " if err != nil { return false, err }" << endl << indent() << " if processor, ok := p.GetProcessorFunction(name); ok {" << endl << indent() << " return processor.Process(seqId, iprot, oprot)" << endl << indent() << " }" << endl << indent() << " iprot.Skip(thrift.STRUCT)" << endl << indent() << " iprot.ReadMessageEnd()" << endl << indent() << " " << x << " := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \" + name)" << endl << indent() << " oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)" << endl << indent() << " " << x << ".Write(oprot)" << endl << indent() << " oprot.WriteMessageEnd()" << endl << indent() << " oprot.Flush()" << endl << indent() << " return false, " << x << endl << indent() << "" << endl << indent() << "}" << endl << endl; } else { f_service_ << indent() << "type " << serviceName << "Processor struct {" << endl << indent() << " *" << extends_processor << endl << indent() << "}" << endl << endl << indent() << "func New" << serviceName << "Processor(handler " << serviceName << ") *" << serviceName << "Processor {" << endl << 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_service_ << indent() << " " << self << ".AddToProcessorMap(\"" << escapedFuncName << "\", &" << pServiceName << "Processor" << publicize((*f_iter)->get_name()) << "{handler:handler})" << endl; } f_service_ << indent() << " return " << self << endl << 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_service_ << 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"; string resultname = publicize(tfunction->get_name()) + "Result"; //t_struct* xs = tfunction->get_xceptions(); //const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; f_service_ << indent() << "type " << processorName << " struct {" << endl << indent() << " handler " << publicize(tservice->get_name()) << endl << indent() << "}" << endl << endl << indent() << "func (p *" << processorName << ") Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {" << endl; indent_up(); f_service_ << indent() << "args := New" << argsname << "()" << endl << indent() << "if err = args.Read(iprot); err != nil {" << endl << indent() << " iprot.ReadMessageEnd()" << endl << indent() << " x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())" << endl << indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.EXCEPTION, seqId)" << endl << indent() << " x.Write(oprot)" << endl << indent() << " oprot.WriteMessageEnd()" << endl << indent() << " oprot.Flush()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "iprot.ReadMessageEnd()" << endl << indent() << "result := New" << resultname << "()" << endl << indent() << "if "; if (!tfunction->is_oneway()) { if (!tfunction->get_returntype()->is_void()) { f_service_ << "result.Success, "; } t_struct* exceptions = tfunction->get_xceptions(); const vector& fields = exceptions->get_members(); vector::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { f_service_ << "result." << publicize(variable_name_to_go_name((*f_iter)->get_name())) << ", "; } } // Generate the function call t_struct* arg_struct = tfunction->get_arglist(); const std::vector& fields = arg_struct->get_members(); vector::const_iterator f_iter; f_service_ << "err = p.handler." << publicize(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." << publicize(variable_name_to_go_name((*f_iter)->get_name())); } f_service_ << "); err != nil {" << endl << indent() << " x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing " << escape_string(tfunction->get_name()) << ": \" + err.Error())" << endl << indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.EXCEPTION, seqId)" << endl << indent() << " x.Write(oprot)" << endl << indent() << " oprot.WriteMessageEnd()" << endl << indent() << " oprot.Flush()" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "if err2 := oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqId); err2 != nil {" << endl << indent() << " err = err2" << endl << indent() << "}" << endl << indent() << "if err2 := result.Write(oprot); err == nil && err2 != nil {" << endl << indent() << " err = err2" << endl << indent() << "}" << endl << indent() << "if err2 := oprot.WriteMessageEnd(); err == nil && err2 != nil {" << endl << indent() << " err = err2" << endl << indent() << "}" << endl << indent() << "if err2 := oprot.Flush(); err == nil && err2 != nil {" << endl << indent() << " err = err2" << endl << indent() << "}" << endl << indent() << "if err != nil {" << endl << indent() << " return" << endl << indent() << "}" << endl << indent() << "return true, err" << endl; indent_down(); f_service_ << indent() << "}" << endl << endl; /* indent(f_service_) << "func (p *" << publicize(tservice->get_name()) << "Client) WriteResultsSuccess" << publicize(tfunction->get_name()) << "(success bool, result " << publicize(tfunction->get_name()) << "Result, seqid int32, oprot thrift.TProtocol) (err error) {" << endl; indent_up(); f_service_ << indent() << "result.Success = success" << endl << indent() << "oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqid)" << endl << indent() << "result.Write(oprot)" << endl << indent() << "oprot.WriteMessageEnd()" << endl << indent() << "oprot.Flush()" << endl << indent() << "return" << endl; indent_down(); f_service_ << indent() << "}" << endl << endl; */ // Try block for a function with exceptions /* if (!tfunction->is_oneway() && xceptions.size() > 0) { indent(f_service_) << "func (p *" << publicize(tservice->get_name()) << "Client) WriteResultsException" << publicize(tfunction->get_name()) << "(error *" << publicize(tfunction->get_name()) << ", result *, seqid, oprot) (err error) {" << endl; indent_up(); // Kinda absurd for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << ", " << (*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(); } else { f_service_ << indent() << "pass" << endl; } } f_service_ << indent() << "err = oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqid)" << endl << indent() << "if err != nil { return err }" << endl << indent() << "err = result.Write(oprot)" << endl << indent() << "if err != nil { return err }" << endl << indent() << "err = oprot.WriteMessageEnd()" << endl << indent() << "if err != nil { return err }" << endl << indent() << "err = oprot.Flush()" << endl << indent() << "if err != nil { return err }" << endl; indent_down(); f_service_ << "}" << endl << endl; } */ } /** * Deserializes a field of any type. */ void t_go_generator::generate_deserialize_field(ofstream &out, t_field* tfield, bool declare, string prefix, bool inclass, bool coerceData) { t_type* orig_type = tfield->get_type(); t_type* type = get_true_type(orig_type); string name(prefix + publicize(variable_name_to_go_name(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, declare, name); } else if (type->is_container()) { generate_deserialize_container(out, type, declare, name); } else if (type->is_base_type() || type->is_enum()) { if (declare) { out << "var " << tfield->get_name() << " " << type_to_go_type(tfield->get_type()) << 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 (((t_base_type*)type)->is_binary()) { out << "ReadBinary()"; } else { out << "ReadString()"; } break; case t_base_type::TYPE_BOOL: out << "ReadBool()"; break; case t_base_type::TYPE_BYTE: 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 << indent() << "return fmt.Errorf(\"error reading field " << tfield->get_key() << ": %s\")" << 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_BYTE) { wrap = "int8"; } if (wrap == "") { indent(out) << name << " = v" << endl; } else { indent(out) << name << " = " << wrap << "(v)" << 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(ofstream &out, t_struct* tstruct, bool declare, string prefix) { string eq(" := "); if (!declare) { eq = " = "; } out << indent() << prefix << eq << new_prefix(type_name(tstruct)) << "()" << endl << indent() << "if err := " << prefix << ".Read(iprot); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T error reading struct: %s\", " << prefix << ")" << endl << indent() << "}" << endl; } /** * Serialize a container by writing out the header followed by * data and then a footer. */ void t_go_generator::generate_deserialize_container(ofstream &out, t_type* ttype, bool declare, string prefix) { string eq(" = "); if (declare) { eq = " := "; } // Declare variables, read header if (ttype->is_map()) { t_map* t = (t_map*)ttype; out << indent() << "_, _, size, err := iprot.ReadMapBegin()" << endl << indent() << "if err != nil {" << endl << indent() << " return fmt.Errorf(\"error reading map begin: %s\")" << endl << indent() << "}" << endl << indent() << prefix << eq << "make(map[" << type_to_go_type(t->get_key_type()) << "]" << type_to_go_type(t->get_val_type()) << ", size)" << endl; } else if (ttype->is_set()) { t_set* t = (t_set*)ttype; out << indent() << "_, size, err := iprot.ReadSetBegin()" << endl << indent() << "if err != nil {" << endl << indent() << " return fmt.Errorf(\"error reading set being: %s\")" << endl << indent() << "}" << endl << indent() << prefix << eq << "make(map[" << type_to_go_type(t->get_elem_type()) << "]bool, size)" << endl; } else if (ttype->is_list()) { t_list* t = (t_list*)ttype; out << indent() << "_, size, err := iprot.ReadListBegin()" << endl << indent() << "if err != nil {" << endl << indent() << " return fmt.Errorf(\"error reading list being: %s\")" << endl << indent() << "}" << endl << indent() << prefix << eq << "make(" << type_to_go_type(t) << ", 0, size)" << 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 (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 << indent() << " return fmt.Errorf(\"error reading map end: %s\")" << endl << indent() << "}" << endl; } else if (ttype->is_set()) { out << indent() << "if err := iprot.ReadSetEnd(); err != nil {" << endl << indent() << " return fmt.Errorf(\"error reading set end: %s\")" << endl << indent() << "}" << endl; } else if (ttype->is_list()) { out << indent() << "if err := iprot.ReadListEnd(); err != nil {" << endl << indent() << " return fmt.Errorf(\"error reading list end: %s\")" << endl << indent() << "}" << endl; } } /** * Generates code to deserialize a map */ void t_go_generator::generate_deserialize_map_element(ofstream &out, t_map* tmap, bool declare, 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, true); generate_deserialize_field(out, &fval, true); indent(out) << prefix << "[" << key << "] = " << val << endl; } /** * Write a set element */ void t_go_generator::generate_deserialize_set_element(ofstream &out, t_set* tset, bool declare, string prefix) { string elem = tmp("_elem"); t_field felem(tset->get_elem_type(), elem); generate_deserialize_field(out, &felem, true, ""); indent(out) << prefix << "[" << elem << "] = true" << endl; } /** * Write a list element */ void t_go_generator::generate_deserialize_list_element(ofstream &out, t_list* tlist, bool declare, string prefix) { string elem = tmp("_elem"); t_field felem(tlist->get_elem_type(), elem); generate_deserialize_field(out, &felem, 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(ofstream &out, t_field* tfield, string prefix) { t_type* type = get_true_type(tfield->get_type()); string name(prefix + publicize(variable_name_to_go_name(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, name); } else if (type->is_base_type() || type->is_enum()) { indent(out) << "if err := 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 (((t_base_type*)type)->is_binary()) { 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_BYTE: out << "WriteByte(byte(" << 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 << indent() << "return fmt.Errorf(\"%T." << escape_string(tfield->get_name()) << " (" << tfield->get_key() << ") field write error: %s\", p) }" << 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(ofstream &out, t_struct* tstruct, string prefix) { out << indent() << "if err := " << prefix << ".Write(oprot); err != nil {" << endl << indent() << " return fmt.Errorf(\"%T error writing struct: %s\", " << prefix << ")" << endl << indent() << "}" << endl; } void t_go_generator::generate_serialize_container(ofstream &out, t_type* ttype, string 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 << indent() << " return fmt.Errorf(\"error writing map begin: %s\")" << endl << 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 << indent() << " return fmt.Errorf(\"error writing set begin: %s\")" << endl << 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 << indent() << " return fmt.Errorf(\"error writing list begin: %s\")" << endl << 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 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 << indent() << " return fmt.Errorf(\"error writing map end: %s\")" << endl << indent() << "}" << endl; } else if (ttype->is_set()) { out << indent() << "if err := oprot.WriteSetEnd(); err != nil {" << endl << indent() << " return fmt.Errorf(\"error writing set end: %s\")" << endl << indent() << "}" << endl; } else if (ttype->is_list()) { out << indent() << "if err := oprot.WriteListEnd(); err != nil {" << endl << indent() << " return fmt.Errorf(\"error writing list end: %s\")" << endl << indent() << "}" << endl; } } /** * Serializes the members of a map. * */ void t_go_generator::generate_serialize_map_element(ofstream &out, t_map* tmap, string kiter, string viter) { t_field kfield(tmap->get_key_type(), ""); generate_serialize_field(out, &kfield, kiter); t_field vfield(tmap->get_val_type(), ""); generate_serialize_field(out, &vfield, viter); } /** * Serializes the members of a set. */ void t_go_generator::generate_serialize_set_element(ofstream &out, t_set* tset, string prefix) { t_field efield(tset->get_elem_type(), ""); generate_serialize_field(out, &efield, prefix); } /** * Serializes the members of a list. */ void t_go_generator::generate_serialize_list_element(ofstream &out, t_list* tlist, string prefix) { t_field efield(tlist->get_elem_type(), ""); generate_serialize_field(out, &efield, prefix); } /** * Generates the docstring for a given struct. */ void t_go_generator::generate_go_docstring(ofstream& 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(ofstream& 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(ofstream& 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& fields = tstruct->get_members(); if (fields.size() > 0) { if (has_doc) { ss << endl; } has_doc = true; ss << subheader << ":\n"; vector::const_iterator p_iter; for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { t_field* p = *p_iter; ss << " - " << publicize(variable_name_to_go_name(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(ofstream& 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 field default value, returns nil otherwise. * * @param tfield The field */ string t_go_generator::render_field_default_value(t_field* tfield, const string& name) { t_type* type = get_true_type(tfield->get_type()); if (tfield->get_value() != NULL) { return render_const_value(type, tfield->get_value(), name); } else { return "nil"; } } /** * 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) { // TODO(mcslee): Nitpicky, no ',' if argument_list is empty string signature = publicize(prefix + tfunction->get_name()) + "("; signature += argument_list(tfunction->get_arglist()) + ") ("; 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 (errs.size() > 0) { signature += errs; if (addError) { 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& fields = tstruct->get_members(); vector::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) { t_program* program = ttype->get_program(); if (program != NULL && program != program_) { string module(get_real_go_module(program)); // for namespaced includes, only keep part after dot. size_t dot = module.rfind('.'); if (dot != string::npos) { module = module.substr(dot + 1); } return module + "." + ttype->get_name(); } return ttype->get_name(); } /** * 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: if (((t_base_type*)type)->is_binary()) { return "thrift.BINARY"; } return "thrift.STRING"; case t_base_type::TYPE_BOOL: return "thrift.BOOL"; case t_base_type::TYPE_BYTE: 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(); } 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."; } return type_to_go_type(type); } /** * Converts the parse type to a go tyoe */ string t_go_generator::type_to_go_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: throw ""; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { return "[]byte"; } return "string"; case t_base_type::TYPE_BOOL: return "bool"; case t_base_type::TYPE_BYTE: 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 "float64"; } } else if (type->is_enum()) { return publicize(type_name(type)); } else if (type->is_struct() || type->is_xception()) { return string("*") + 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 string("map[") + keyType + "]" + valueType; } else if (type->is_set()) { t_set* t = (t_set*)type; string elemType = type_to_go_key_type(t->get_elem_type()); return string("map[") + elemType + string("]bool"); } else if (type->is_list()) { t_list* t = (t_list*)type; string elemType = type_to_go_type(t->get_elem_type()); return string("[]") + elemType; } else if (type->is_typedef()) { return publicize(type_name(type)); } throw "INVALID TYPE IN type_to_go_type: " + type->get_name(); } /** * Converts the parse type to a go tyoe */ bool t_go_generator::can_be_nil(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 for can_be_nil"; case t_base_type::TYPE_BOOL: case t_base_type::TYPE_BYTE: 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 false; case t_base_type::TYPE_STRING: return (((t_base_type*)type)->is_binary()); } } else if (type->is_enum()) { return false; } else if (type->is_struct() || type->is_xception()) { return true; } else if (type->is_map()) { return true; } else if (type->is_set()) { return true; } else if (type->is_list()) { return true; } throw "INVALID TYPE IN can_be_nil: " + 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) { 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") thrift-compiler_0.9.1/cpp/src/generate/t_javame_generator.cc0000644000175000017500000034005112203157755025017 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "platform.h" #include "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 /** * Java code generator. * */ class t_javame_generator : public t_oop_generator { public: t_javame_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; std::map::const_iterator iter; out_dir_base_ = "gen-javame"; } /** * Init and close methods */ void init_generator(); void close_generator(); void generate_consts(std::vector consts); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_struct (t_struct* tstruct); void generate_union (t_struct* tunion); void generate_xception(t_struct* txception); void generate_service (t_service* tservice); void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval=false); std::string render_const_value(std::ofstream& 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::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false); void generate_java_struct_equality(std::ofstream& out, t_struct* tstruct); void generate_java_struct_compare_to(std::ofstream& out, t_struct* tstruct); void generate_java_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_java_validator(std::ofstream& out, t_struct* tstruct); void generate_java_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_java_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_java_struct_tostring(std::ofstream& out, t_struct* tstruct); void generate_java_struct_clear(std::ofstream& out, t_struct* tstruct); void generate_field_value_meta_data(std::ofstream& 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::ofstream& out, t_struct* tstruct); void generate_java_bean_boilerplate(std::ofstream& 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(ofstream& 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(ofstream& out, t_struct* tstruct); void generate_union_getters_and_setters(ofstream& out, t_struct* tstruct); void generate_union_abstract_methods(ofstream& out, t_struct* tstruct); void generate_check_type(ofstream& out, t_struct* tstruct); void generate_read_value(ofstream& out, t_struct* tstruct); void generate_write_value(ofstream& out, t_struct* tstruct); void generate_get_field_desc(ofstream& out, t_struct* tstruct); void generate_get_struct_desc(ofstream& out, t_struct* tstruct); void generate_get_field_name(ofstream& out, t_struct* tstruct); void generate_union_comparisons(ofstream& out, t_struct* tstruct); void generate_union_hashcode(ofstream& out, t_struct* tstruct); /** * Serialization constructs */ void generate_deserialize_field (std::ofstream& out, t_field* tfield, std::string prefix=""); void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream& out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter, std::string map); void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream& out, t_list* tlist, std::string iter); void generate_java_doc (std::ofstream& out, t_field* field); void generate_java_doc (std::ofstream& out, t_doc* tdoc); void generate_java_doc (std::ofstream& out, t_function* tdoc); void generate_java_docstring_comment (std::ofstream &out, string contents); void generate_deep_copy_container(std::ofstream& 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::ofstream& 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); void generate_struct_desc(ofstream& out, t_struct* tstruct); void generate_field_descs(ofstream& 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_; std::ofstream 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 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 constants = tenum->get_constants(); vector::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;" <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 consts) { if (consts.empty()) { return; } string f_consts_name = package_dir_+ "/" + program_name_ + "Constants.java"; ofstream 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::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::ofstream& 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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(ofstream& 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_BYTE: 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_BYTE: 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 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 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(ofstream& 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& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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(ofstream& 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(ofstream& 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& members = tstruct->get_members(); vector::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(ofstream& 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& members = tstruct->get_members(); vector::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(ofstream& 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& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { indent(out) << "protected TField getFieldDesc(_Fields setField) {" << endl; indent_up(); const vector& members = tstruct->get_members(); vector::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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream &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& members = tstruct->get_members(); vector::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 other." << 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(ofstream& 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; const vector& members = tstruct->get_members(); vector::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_base_type() && ((t_base_type*)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(ofstream& 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& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { out << indent() << "public void read(TProtocol iprot) throws TException {" << endl; indent_up(); const vector& fields = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct){ indent(out) << "public void validate() throws TException {" << endl; indent_up(); const vector& fields = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { out << indent() << "public void write(TProtocol oprot) throws TException {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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(ofstream& out, t_struct* tstruct) { out << indent() << "public void write(TProtocol oprot) throws TException {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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::ofstream& 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& fields = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::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(ofstream& 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& fields = tstruct->get_members(); vector::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_base_type() && ((t_base_type*)(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_BYTE : 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::ofstream& out, t_type* type){ out << endl; indent_up(); indent_up(); if (type->is_struct()){ 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"; std::ofstream 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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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& fields = arg_struct->get_members(); vector::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 << "\", 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& xceptions = xs->get_members(); vector::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 functions = tservice->get_functions(); vector::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& fields = xs->get_members(); vector::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& xceptions = xs->get_members(); vector::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& fields = arg_struct->get_members(); vector::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(ofstream& 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 (!((t_base_type*)type)->is_binary()) { out << "readString();"; } else { out << "readBinary();"; } break; case t_base_type::TYPE_BOOL: out << "readBool();"; break; case t_base_type::TYPE_BYTE: 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 (((t_base_type*)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_BYTE: 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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_BYTE: 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 C++ 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) { 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_BYTE: 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& xceptions = xs->get_members(); vector::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& fields = tstruct->get_members(); vector::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_BYTE: 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 (string::iterator iter = name.begin(); iter != name.end(); ++iter) { string::value_type character = (*iter); 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(ofstream &out, string contents) { generate_docstring_comment(out, "/**\n", " * ", contents, " */\n"); } void t_javame_generator::generate_java_doc(ofstream &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(ofstream &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(ofstream &out, t_function* tfunction) { if (tfunction->has_doc()) { stringstream ss; ss << tfunction->get_doc(); const vector& fields = tfunction->get_arglist()->get_members(); vector::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(ofstream &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(((t_base_type*)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(ofstream& 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 (((t_base_type*)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(ofstream& 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(ofstream& 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(ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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& members = tstruct->get_members(); vector::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::ofstream& out, t_struct* tstruct) { indent(out) << "public void clear() {" << endl; const vector& members = tstruct->get_members(); vector::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_BYTE: 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", "") thrift-compiler_0.9.1/cpp/src/generate/t_html_generator.h0000644000175000017500000003201412203157755024357 0ustar eevanseevans00000000000000#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;}" thrift-compiler_0.9.1/cpp/src/generate/t_as3_generator.cc0000644000175000017500000025501112203157755024243 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include "platform.h" #include "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 /** * AS3 code generator. * */ class t_as3_generator : public t_oop_generator { public: t_as3_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("bindable"); bindable_ = (iter != parsed_options.end()); out_dir_base_ = "gen-as3"; } /** * Init and close methods */ void init_generator(); void close_generator(); void generate_consts(std::vector consts); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_struct (t_struct* tstruct); void generate_xception(t_struct* txception); void generate_service (t_service* tservice); void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval=false); std::string render_const_value(ofstream& 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::ofstream& 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::ofstream& out, t_struct* tstruct); void generate_as3_validator(std::ofstream& out, t_struct* tstruct); void generate_as3_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_as3_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_as3_struct_tostring(std::ofstream& out, t_struct* tstruct, bool bindable); void generate_as3_meta_data_map(std::ofstream& out, t_struct* tstruct); void generate_field_value_meta_data(std::ofstream& 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::ofstream& out, t_struct* tstruct); void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct); void generate_as3_bean_boilerplate(std::ofstream& 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(ofstream& 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::ofstream& out, t_field* tfield, std::string prefix=""); void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream& out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter, std::string map); void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream& out, t_list* tlist, std::string iter); void generate_as3_doc (std::ofstream& out, t_doc* tdoc); void generate_as3_doc (std::ofstream& 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); 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_; std::ofstream 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 ""; } /** * 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& members = tstruct->get_members(); vector::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& functions = tservice->get_functions(); vector::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 f_enum; f_enum.open(f_enum_name.c_str()); // 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 constants = tenum->get_constants(); vector::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 consts) { if (consts.empty()) { return; } string f_consts_name = package_dir_+ "/" + program_name_ + "Constants.as"; ofstream f_consts; f_consts.open(f_consts_name.c_str()); // 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::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::ofstream& 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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(ofstream& 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_BYTE: 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 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(ofstream &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& members = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { out << indent() << "public function read(iprot:TProtocol):void {" << endl; indent_up(); const vector& fields = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct){ indent(out) << "public function validate():void {" << endl; indent_up(); const vector& fields = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct) { out << indent() << "public function write(oprot:TProtocol):void {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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(); } 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; } } // 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(ofstream& out, t_struct* tstruct) { out << indent() << "public function write(oprot:TProtocol):void {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_sorted_members(); vector::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::ofstream& 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& fields = tstruct->get_members(); vector::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::ofstream& out, t_struct* tstruct){ const vector& fields = tstruct->get_members(); vector::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(ofstream& out, t_struct* tstruct, bool bindable) { const vector& fields = tstruct->get_members(); vector::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(ofstream& 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& fields = tstruct->get_members(); vector::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_base_type() && ((t_base_type*)(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(ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::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_BYTE : 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::ofstream& 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_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) << 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) << 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); 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) <get_extends() != NULL) { extends = type_name(tservice->get_extends()); extends_iface = " extends " + extends; } generate_as3_doc(f_service_, tservice); f_service_ << indent() << "public interface " << service_name_ << extends_iface << " {" << endl << endl; indent_up(); vector functions = tservice->get_functions(); vector::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 functions = tservice->get_functions(); vector::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 = type_name(tservice->get_extends()); 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 functions = tservice->get_functions(); vector::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::const_iterator fld_iter; const vector& fields = arg_struct->get_members(); // Serialize the request f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", 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& xceptions = xs->get_members(); vector::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 functions = tservice->get_functions(); vector::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& fields = xs->get_members(); vector::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& xceptions = xs->get_members(); vector::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& fields = arg_struct->get_members(); vector::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(ofstream& 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 (((t_base_type*)type)->is_binary()) { out << "readBinary();"; } else { out << "readString();"; } break; case t_base_type::TYPE_BOOL: out << "readBool();"; break; case t_base_type::TYPE_BYTE: 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 (((t_base_type*)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_BYTE: 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 */ 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_BYTE: 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 C++ 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) { 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_BYTE: 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& fields = tstruct->get_members(); vector::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_BYTE: 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 (string::iterator iter = name.begin(); iter != name.end(); ++iter) { string::value_type character = (*iter); 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(ofstream &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(ofstream &out, t_function* tfunction) { if (tfunction->has_doc()) { stringstream ss; ss << tfunction->get_doc(); const vector& fields = tfunction->get_arglist()->get_members(); vector::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(ofstream& 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" ) thrift-compiler_0.9.1/cpp/src/generate/t_cocoa_generator.cc0000644000175000017500000027256312203157755024654 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include "t_oop_generator.h" #include "platform.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 /** * Objective-C code generator. * * mostly copy/pasting/tweaking from mcslee's work. */ class t_cocoa_generator : public t_oop_generator { public: t_cocoa_generator( t_program* program, const std::map& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) option_string; std::map::const_iterator iter; iter = parsed_options.find("log_unexpected"); log_unexpected_ = (iter != parsed_options.end()); iter = parsed_options.find("validate_required"); validate_required_ = (iter != parsed_options.end()); out_dir_base_ = "gen-cocoa"; } /** * Init and close methods */ void init_generator(); void close_generator(); void generate_consts(std::vector consts); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_struct (t_struct* tstruct); void generate_xception(t_struct* txception); void generate_service (t_service* tservice); void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool defval=false, bool is_property=false); std::string render_const_value(ofstream& out, t_type* type, t_const_value* value, bool containerize_it=false); void generate_cocoa_struct(t_struct* tstruct, bool is_exception); void generate_cocoa_struct_interface(std::ofstream& out, t_struct* tstruct, bool is_xception=false); void generate_cocoa_struct_implementation(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false); void generate_cocoa_struct_initializer_signature(std::ofstream& out, t_struct* tstruct); void generate_cocoa_struct_init_with_coder_method(ofstream &out, t_struct* tstruct, bool is_exception); void generate_cocoa_struct_encode_with_coder_method(ofstream &out, t_struct* tstruct, bool is_exception); void generate_cocoa_struct_field_accessor_declarations(std::ofstream& out, t_struct* tstruct, bool is_exception); void generate_cocoa_struct_field_accessor_implementations(std::ofstream& out, t_struct* tstruct, bool is_exception); void generate_cocoa_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_cocoa_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_cocoa_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_cocoa_struct_validator(std::ofstream& out, t_struct* tstruct); void generate_cocoa_struct_description(std::ofstream& out, t_struct* tstruct); std::string function_result_helper_struct_type(t_function* tfunction); std::string function_args_helper_struct_type(t_function* tfunction); void generate_function_helpers(t_function* tfunction); /** * Service-level generation functions */ void generate_cocoa_service_protocol (std::ofstream& out, t_service* tservice); void generate_cocoa_service_client_interface (std::ofstream& out, t_service* tservice); void generate_cocoa_service_client_implementation (std::ofstream& out, t_service* tservice); void generate_cocoa_service_server_interface (std::ofstream& out, t_service* tservice); void generate_cocoa_service_server_implementation (std::ofstream& out, t_service* tservice); void generate_cocoa_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::ofstream& out, t_field* tfield, std::string fieldName); void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix=""); void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix=""); void generate_deserialize_list_element (std::ofstream& out, t_list* tlist, std::string prefix=""); void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix=""); void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string fieldName=""); void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter, std::string map); void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter); void generate_serialize_list_element (std::ofstream& out, t_list* tlist, std::string index, std::string listName); /** * Helper rendering functions */ std::string cocoa_prefix(); std::string cocoa_imports(); std::string cocoa_thrift_imports(); std::string type_name(t_type* ttype, bool class_ref=false); std::string base_type_name(t_base_type* tbase); std::string declare_field(t_field* tfield); std::string declare_property(t_field* tfield); std::string function_signature(t_function* tfunction); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); std::string format_string_for_type(t_type* type); std::string call_field_setter(t_field* tfield, std::string fieldName); std::string containerize(t_type * ttype, std::string fieldName); std::string decontainerize(t_field * tfield, std::string fieldName); 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(); } private: std::string cocoa_prefix_; std::string constants_declarations_; /** * File streams */ std::ofstream f_header_; std::ofstream f_impl_; bool log_unexpected_; bool validate_required_; }; /** * Prepares for file generation by opening up the necessary file output * streams. */ void t_cocoa_generator::init_generator() { // Make output directory MKDIR(get_out_dir().c_str()); cocoa_prefix_ = program_->get_namespace("cocoa"); // we have a .h header file... string f_header_name = program_name_+".h"; string f_header_fullname = get_out_dir()+f_header_name; f_header_.open(f_header_fullname.c_str()); f_header_ << autogen_comment() << endl; f_header_ << cocoa_imports() << cocoa_thrift_imports(); // ...and a .m implementation file string f_impl_name = get_out_dir()+program_name_+".m"; f_impl_.open(f_impl_name.c_str()); f_impl_ << autogen_comment() << endl; f_impl_ << cocoa_imports() << cocoa_thrift_imports() << "#import \"" << f_header_name << "\"" << endl << endl; } /** * Prints standard Cocoa imports * * @return List of imports for Cocoa libraries */ string t_cocoa_generator::cocoa_imports() { return string() + "#import \n" + "\n"; } /** * Prints thrift runtime imports * * @return List of imports necessary for thrift runtime */ string t_cocoa_generator::cocoa_thrift_imports() { string result = string() + "#import \"TProtocol.h\"\n" + "#import \"TApplicationException.h\"\n" + "#import \"TProtocolException.h\"\n" + "#import \"TProtocolUtil.h\"\n" + "#import \"TProcessor.h\"\n" + "#import \"TObjective-C.h\"\n" + "#import \"TBase.h\"\n" + "\n"; // Include other Thrift includes const vector& includes = program_->get_includes(); for (size_t i = 0; i < includes.size(); ++i) { result += "#import \"" + includes[i]->get_name() + ".h\"" + "\n"; } result += "\n"; return result; } /** * Finish up generation. */ void t_cocoa_generator::close_generator() { // stick our constants declarations at the end of the header file // since they refer to things we are defining. f_header_ << constants_declarations_ << endl; } /** * Generates a typedef. This is just a simple 1-liner in objective-c * * @param ttypedef The type definition */ void t_cocoa_generator::generate_typedef(t_typedef* ttypedef) { f_header_ << indent() << "typedef " << type_name(ttypedef->get_type()) << " " << cocoa_prefix_ << ttypedef->get_symbolic() << ";" << endl << endl; } /** * Generates code for an enumerated type. In Objective-C, this is * essentially the same as the thrift definition itself, using the * enum keyword in Objective-C. For namespace purposes, the name of * the enum plus an underscore is prefixed onto each element. * * @param tenum The enumeration */ void t_cocoa_generator::generate_enum(t_enum* tenum) { f_header_ << indent() << "enum " << cocoa_prefix_ << tenum->get_name() << " {" << endl; indent_up(); vector constants = tenum->get_constants(); vector::iterator c_iter; bool first = true; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { if (first) { first = false; } else { f_header_ << "," << endl; } f_header_ << indent() << tenum->get_name() << "_" << (*c_iter)->get_name(); f_header_ << " = " << (*c_iter)->get_value(); } indent_down(); f_header_ << endl << "};" << endl << endl; } /** * Generates a class that holds all the constants. Primitive values * could have been placed outside this class, but I just put * everything in for consistency. */ void t_cocoa_generator::generate_consts(std::vector consts) { std::ostringstream const_interface; string constants_class_name = cocoa_prefix_ + program_name_ + "Constants"; const_interface << "@interface " << constants_class_name << " : NSObject "; scope_up(const_interface); scope_down(const_interface); // getter method for each constant defined. vector::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(); const_interface << "+ (" << type_name(type) << ") " << name << ";" << endl; } const_interface << "@end"; // this gets spit into the header file in ::close_generator constants_declarations_ = const_interface.str(); // static variables in the .m hold all constant values 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_impl_ << "static " << type_name(type) << " " << cocoa_prefix_ << name; if (!type->is_container() && !type->is_struct()) { f_impl_ << " = " << render_const_value(f_impl_, type, (*c_iter)->get_value()); } f_impl_ << ";" << endl; } f_impl_ << endl; f_impl_ << "@implementation " << constants_class_name << endl; // initialize complex constants when the class is loaded f_impl_ << "+ (void) initialize "; scope_up(f_impl_); for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { if ((*c_iter)->get_type()->is_container() || (*c_iter)->get_type()->is_struct()) { print_const_value(f_impl_, cocoa_prefix_+(*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false, false); f_impl_ << ";" << endl; } } scope_down(f_impl_); // getter method for each constant 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_impl_ << "+ (" << type_name(type) << ") " << name; scope_up(f_impl_); indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl; scope_down(f_impl_); } f_impl_ << "@end" << endl << endl; } /** * Generates a struct definition for a thrift data type. This is a class * with protected data members, read(), write(), and getters and setters. * * @param tstruct The struct definition */ void t_cocoa_generator::generate_struct(t_struct* tstruct) { generate_cocoa_struct_interface(f_header_, tstruct, false); generate_cocoa_struct_implementation(f_impl_, tstruct, false); } /** * Exceptions are structs, but they inherit from NSException * * @param tstruct The struct definition */ void t_cocoa_generator::generate_xception(t_struct* txception) { generate_cocoa_struct_interface(f_header_, txception, true); generate_cocoa_struct_implementation(f_impl_, txception, true); } /** * Generate the interface for a struct * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_interface(ofstream &out, t_struct* tstruct, bool is_exception) { out << "@interface " << cocoa_prefix_ << tstruct->get_name() << " : "; if (is_exception) { out << "NSException "; } else { out << "NSObject "; } out << " "; scope_up(out); // members are protected. this is redundant, but explicit. // f_header_ << endl << "@protected:" << endl; const vector& members = tstruct->get_members(); // member varialbes vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { out << indent() << declare_field(*m_iter) << endl; } if (members.size() > 0) { out << endl; // isset fields for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { indent(out) << "BOOL __" << (*m_iter)->get_name() << "_isset;" << endl; } } scope_down(out); out << endl; // properties if (members.size() > 0) { out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { out << indent() << declare_property(*m_iter) << endl; } out << "#endif" << endl << endl; } // default initializer out << indent() << "- (id) init;" << endl; // initializer for all fields if (!members.empty()) { generate_cocoa_struct_initializer_signature(out, tstruct); out << ";" << endl; } out << endl; // read and write out << "- (void) read: (id ) inProtocol;" << endl; out << "- (void) write: (id ) outProtocol;" << endl; out << endl; // validator out << "- (void) validate;" << endl << endl; // getters and setters generate_cocoa_struct_field_accessor_declarations(out, tstruct, is_exception); out << "@end" << endl << endl; } /** * Generate signature for initializer of struct with a parameter for * each field. */ void t_cocoa_generator::generate_cocoa_struct_initializer_signature(ofstream &out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::const_iterator m_iter; indent(out) << "- (id) initWith"; for (m_iter = members.begin(); m_iter != members.end(); ) { if (m_iter == members.begin()) { out << capitalize((*m_iter)->get_name()); } else { out << (*m_iter)->get_name(); } out << ": (" << type_name((*m_iter)->get_type()) << ") " << (*m_iter)->get_name(); ++m_iter; if (m_iter != members.end()) { out << " "; } } } /** * Generate getter and setter declarations for all fields, plus an * IsSet getter. */ void t_cocoa_generator::generate_cocoa_struct_field_accessor_declarations(ofstream &out, t_struct* tstruct, bool is_exception) { (void) is_exception; const vector& members = tstruct->get_members(); vector::const_iterator m_iter; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { out << indent() << "#if !__has_feature(objc_arc)" << endl; out << indent() << "- (" << type_name((*m_iter)->get_type()) << ") " << decapitalize((*m_iter)->get_name()) << ";" << endl; out << indent() << "- (void) set" << capitalize((*m_iter)->get_name()) << ": (" << type_name((*m_iter)->get_type()) << ") " << (*m_iter)->get_name() << ";" << endl; out << indent() << "#endif" << endl; out << indent() << "- (BOOL) " << (*m_iter)->get_name() << "IsSet;" << endl << endl; } } /** * Generate the initWithCoder method for this struct so it's compatible with * the NSCoding protocol */ void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream &out, t_struct* tstruct, bool is_exception) { indent(out) << "- (id) initWithCoder: (NSCoder *) decoder" << endl; scope_up(out); if (is_exception) { // NSExceptions conform to NSCoding, so we can call super out << indent() << "self = [super initWithCoder: decoder];" << endl; } else { out << indent() << "self = [super init];" << endl; } const vector& members = tstruct->get_members(); vector::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()); out << indent() << "if ([decoder containsValueForKey: @\""<< (*m_iter)->get_name() <<"\"])" << endl; scope_up(out); out << indent() << "__" << (*m_iter)->get_name() << " = "; if (type_can_be_null(t)) { out << "[[decoder decodeObjectForKey: @\"" << (*m_iter)->get_name() << "\"] retain_stub];" << endl; } else if (t->is_enum()) { out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; } else { t_base_type::t_base tbase = ((t_base_type *) t)->get_base(); switch (tbase) { case t_base_type::TYPE_BOOL: out << "[decoder decodeBoolForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_BYTE: out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I16: out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I32: out << "[decoder decodeInt32ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I64: out << "[decoder decodeInt64ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_DOUBLE: out << "[decoder decodeDoubleForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; default: throw "compiler error: don't know how to decode thrift type: " + t_base_type::t_base_name(tbase); } } out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl; scope_down(out); } out << indent() << "return self;" << endl; scope_down(out); out << endl; } /** * Generate the encodeWithCoder method for this struct so it's compatible with * the NSCoding protocol */ void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ofstream &out, t_struct* tstruct, bool is_exception) { indent(out) << "- (void) encodeWithCoder: (NSCoder *) encoder" << endl; scope_up(out); if (is_exception) { // NSExceptions conform to NSCoding, so we can call super out << indent() << "[super encodeWithCoder: encoder];" << endl; } const vector& members = tstruct->get_members(); vector::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()); out << indent() << "if (__"<< (*m_iter)->get_name() <<"_isset)" << endl; scope_up(out); //out << indent() << "__" << (*m_iter)->get_name() << " = "; if (type_can_be_null(t)) { out << indent() << "[encoder encodeObject: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; } else if (t->is_enum()) { out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; } else { t_base_type::t_base tbase = ((t_base_type *) t)->get_base(); switch (tbase) { case t_base_type::TYPE_BOOL: out << indent() << "[encoder encodeBool: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_BYTE: out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I16: out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I32: out << indent() << "[encoder encodeInt32: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_I64: out << indent() << "[encoder encodeInt64: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; case t_base_type::TYPE_DOUBLE: out << indent() << "[encoder encodeDouble: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl; break; default: throw "compiler error: don't know how to encode thrift type: " + t_base_type::t_base_name(tbase); } } scope_down(out); } scope_down(out); out << endl; } /** * Generate struct implementation. * * @param tstruct The struct definition * @param is_exception Is this an exception? * @param is_result If this is a result it needs a different writer */ void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream &out, t_struct* tstruct, bool is_exception, bool is_result) { indent(out) << "@implementation " << cocoa_prefix_ << tstruct->get_name() << endl << endl; const vector& members = tstruct->get_members(); vector::const_iterator m_iter; // exceptions need to call the designated initializer on NSException if (is_exception) { out << indent() << "- (id) init" << endl; scope_up(out); out << indent() << "return [super initWithName: @\"" << tstruct->get_name() << "\" reason: @\"unknown\" userInfo: nil];" << endl; scope_down(out); out << endl; } else { // struct // default initializer // setup instance variables with default values indent(out) << "- (id) init" << endl; scope_up(out); indent(out) << "self = [super init];" << endl; if (members.size() > 0) { out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" << endl; 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, "self."+(*m_iter)->get_name(), t, (*m_iter)->get_value(), false, true); } } out << "#endif" << endl; } indent(out) << "return self;" << endl; scope_down(out); out << endl; } // initializer with all fields as params if (!members.empty()) { generate_cocoa_struct_initializer_signature(out, tstruct); out << endl; scope_up(out); if (is_exception) { out << indent() << "self = [self init];" << endl; } else { out << indent() << "self = [super init];" << endl; } for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); out << indent() << "__" << (*m_iter)->get_name() << " = "; if (type_can_be_null(t)) { out << "[" << (*m_iter)->get_name() << " retain_stub];" << endl; } else { out << (*m_iter)->get_name() << ";" << endl; } out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl; } out << indent() << "return self;" << endl; scope_down(out); out << endl; } // initWithCoder for NSCoding generate_cocoa_struct_init_with_coder_method(out, tstruct, is_exception); // encodeWithCoder for NSCoding generate_cocoa_struct_encode_with_coder_method(out, tstruct, is_exception); // dealloc if (!members.empty()) { out << "- (void) dealloc" << endl; 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 (type_can_be_null(t)) { indent(out) << "[__" << (*m_iter)->get_name() << " release_stub];" << endl; } } out << indent() << "[super dealloc_stub];" << endl; scope_down(out); out << endl; } // the rest of the methods generate_cocoa_struct_field_accessor_implementations(out, tstruct, is_exception); generate_cocoa_struct_reader(out, tstruct); if (is_result) { generate_cocoa_struct_result_writer(out, tstruct); } else { generate_cocoa_struct_writer(out, tstruct); } generate_cocoa_struct_validator(out, tstruct); generate_cocoa_struct_description(out, tstruct); out << "@end" << endl << endl; } /** * Generates a function to read all the fields of the struct. * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out, t_struct* tstruct) { out << "- (void) read: (id ) inProtocol" << endl; scope_up(out); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; // Declare stack tmp variables indent(out) << "NSString * fieldName;" << endl; indent(out) << "int fieldType;" << endl; indent(out) << "int fieldID;" << endl; out << endl; indent(out) << "[inProtocol readStructBeginReturningName: NULL];" << endl; // Loop over reading in fields indent(out) << "while (true)" << endl; scope_up(out); // Read beginning field marker indent(out) << "[inProtocol readFieldBeginReturningName: &fieldName type: &fieldType fieldID: &fieldID];" << endl; // Check for field STOP marker and break indent(out) << "if (fieldType == 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 (fieldID)" << 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 (fieldType == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; indent_up(); generate_deserialize_field(out, *f_iter, "fieldValue"); indent(out) << call_field_setter(*f_iter, "fieldValue") << endl; // if this is an allocated field, release it since the struct // is now retaining it if (type_can_be_null((*f_iter)->get_type())) { // deserialized strings are autorelease, so don't release them if (!(get_true_type((*f_iter)->get_type())->is_string())) { indent(out) << "[fieldValue release_stub];" << endl; } } indent_down(); out << indent() << "} else { " << endl; if (log_unexpected_) { out << indent() << " NSLog(@\"%s: field ID %i has unexpected type %i. Skipping.\", __PRETTY_FUNCTION__, fieldID, fieldType);" << endl; } out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl << indent() << "}" << endl << indent() << "break;" << endl; indent_down(); } // In the default case we skip the field out << indent() << "default:" << endl; if (log_unexpected_) { out << indent() << " NSLog(@\"%s: unexpected field ID %i with type %i. Skipping.\", __PRETTY_FUNCTION__, fieldID, fieldType);" << endl; } out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl << indent() << " break;" << endl; scope_down(out); // Read field end marker indent(out) << "[inProtocol readFieldEnd];" << endl; scope_down(out); out << indent() << "[inProtocol readStructEnd];" << endl; // performs various checks (e.g. check that all required fields are set) if (validate_required_) { out << indent() << "[self validate];" << endl; } indent_down(); out << indent() << "}" << endl << endl; } /** * Generates a function to write all the fields of the struct * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_writer(ofstream& out, t_struct* tstruct) { out << indent() << "- (void) write: (id ) outProtocol {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; out << indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { out << indent() << "if (__" << (*f_iter)->get_name() << "_isset) {" << endl; indent_up(); bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { out << indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl; indent_up(); } indent(out) << "[outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) << " fieldID: " << (*f_iter)->get_key() << "];" << endl; // Write field contents generate_serialize_field(out, *f_iter, "__"+(*f_iter)->get_name()); // Write field closer indent(out) << "[outProtocol writeFieldEnd];" << endl; if (null_allowed) { scope_down(out); } scope_down(out); } // Write the struct map out << indent() << "[outProtocol writeFieldStop];" << endl << indent() << "[outProtocol 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, and only one of them can be set at a time. * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out, t_struct* tstruct) { out << indent() << "- (void) write: (id ) outProtocol {" << endl; indent_up(); string name = tstruct->get_name(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; out << indent() << "[outProtocol writeStructBeginWithName: @\"" << 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 << "(__" << (*f_iter)->get_name() << "_isset) {" << endl; indent_up(); bool null_allowed = type_can_be_null((*f_iter)->get_type()); if (null_allowed) { out << indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl; indent_up(); } indent(out) << "[outProtocol writeFieldBeginWithName: @\"" << (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) << " fieldID: " << (*f_iter)->get_key() << "];" << endl; // Write field contents generate_serialize_field(out, *f_iter, "__"+(*f_iter)->get_name()); // Write field closer indent(out) << "[outProtocol writeFieldEnd];" << endl; if (null_allowed) { indent_down(); indent(out) << "}" << endl; } indent_down(); indent(out) << "}"; } // Write the struct map out << endl << indent() << "[outProtocol writeFieldStop];" << endl << indent() << "[outProtocol writeStructEnd];" << endl; indent_down(); out << indent() << "}" << endl << endl; } /** * Generates a function to perform various checks * (e.g. check that all required fields are set) * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_validator(ofstream& out, t_struct* tstruct) { out << indent() << "- (void) validate {" << endl; indent_up(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; out << indent() << "// check for required fields" << endl; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_field* field = (*f_iter); if ((*f_iter)->get_req() == t_field::T_REQUIRED) { out << indent() << "if (!__" << field->get_name() << "_isset) {" << endl << indent() << " @throw [TProtocolException exceptionWithName: @\"TProtocolException\"" << endl << indent() << " reason: @\"Required field '" << (*f_iter)->get_name() << "' is not set.\"];" << endl << indent() << "}" << endl; } } indent_down(); out << indent() << "}" << endl << endl; } /** * Generate property accessor methods for all fields in the struct. * getter, setter, isset getter. * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ofstream& out, t_struct* tstruct, bool is_exception) { (void) is_exception; const vector& fields = tstruct->get_members(); vector::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 = field_name; cap_name[0] = toupper(cap_name[0]); // Simple getter indent(out) << "- (" << type_name(type) << ") "; out << field_name << " {" << endl; indent_up(); if (!type_can_be_null(type)) { indent(out) << "return __" << field_name << ";" << endl; } else { indent(out) << "return [[__" << field_name << " retain_stub] autorelease_stub];" << endl; } indent_down(); indent(out) << "}" << endl << endl; // Simple setter indent(out) << "- (void) set" << cap_name << ": (" << type_name(type) << ") " << field_name << " {" << endl; indent_up(); if (!type_can_be_null(type)) { indent(out) << "__" << field_name << " = " << field_name << ";" << endl; } else { indent(out) << "[" << field_name << " retain_stub];" << endl; indent(out) << "[__" << field_name << " release_stub];" << endl; indent(out) << "__" << field_name << " = " << field_name << ";" << endl; } indent(out) << "__" << field_name << "_isset = YES;" << endl; indent_down(); indent(out) << "}" << endl << endl; // IsSet indent(out) << "- (BOOL) " << field_name << "IsSet {" << endl; indent_up(); indent(out) << "return __" << field_name << "_isset;" << endl; indent_down(); indent(out) << "}" << endl << endl; // Unsetter - do we need this? indent(out) << "- (void) unset" << cap_name << " {" << endl; indent_up(); if (type_can_be_null(type)) { indent(out) << "[__" << field_name << " release_stub];" << endl; indent(out) << "__" << field_name << " = nil;" << endl; } indent(out) << "__" << field_name << "_isset = NO;" << endl; indent_down(); indent(out) << "}" << endl << endl; } } /** * Generates a description method for the given struct * * @param tstruct The struct definition */ void t_cocoa_generator::generate_cocoa_struct_description(ofstream& out, t_struct* tstruct) { out << indent() << "- (NSString *) description {" << endl; indent_up(); out << indent() << "NSMutableString * ms = [NSMutableString stringWithString: @\"" << tstruct->get_name() << "(\"];" << endl; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; indent(out) << "[ms appendString: @\"" << (*f_iter)->get_name() << ":\"];" << endl; } else { indent(out) << "[ms appendString: @\"," << (*f_iter)->get_name() << ":\"];" << endl; } t_type* ttype = (*f_iter)->get_type(); indent(out) << "[ms appendFormat: @\"" << format_string_for_type(ttype) << "\", __" << (*f_iter)->get_name() << "];" << endl; } out << indent() << "[ms appendString: @\")\"];" << endl << indent() << "return [NSString stringWithString: ms];" << endl; indent_down(); indent(out) << "}" << endl << endl; } /** * Generates a thrift service. In Objective-C this consists of a * protocol definition, a client interface and a client implementation. * * @param tservice The service definition */ void t_cocoa_generator::generate_service(t_service* tservice) { generate_cocoa_service_protocol(f_header_, tservice); generate_cocoa_service_client_interface(f_header_, tservice); generate_cocoa_service_server_interface(f_header_, tservice); generate_cocoa_service_helpers(tservice); generate_cocoa_service_client_implementation(f_impl_, tservice); generate_cocoa_service_server_implementation(f_impl_, tservice); } /** * Generates structs for all the service return types * * @param tservice The service */ void t_cocoa_generator::generate_cocoa_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); generate_cocoa_struct_interface(f_impl_, ts, false); generate_cocoa_struct_implementation(f_impl_, ts, false, false); generate_function_helpers(*f_iter); } } string t_cocoa_generator::function_result_helper_struct_type(t_function* tfunction) { if (tfunction->is_oneway()) { return capitalize(tfunction->get_name()); } else { return capitalize(tfunction->get_name()) + "_result"; } } string t_cocoa_generator::function_args_helper_struct_type(t_function* tfunction) { return tfunction->get_name() + "_args"; } /** * Generates a struct and helpers for a function. * * @param tfunction The function */ void t_cocoa_generator::generate_function_helpers(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(tfunction)); 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& fields = xs->get_members(); vector::const_iterator f_iter; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { result.append(*f_iter); } // generate the result struct generate_cocoa_struct_interface(f_impl_, &result, false); generate_cocoa_struct_implementation(f_impl_, &result, false, true); } /** * Generates a service protocol definition. * * @param tservice The service to generate a protocol definition for */ void t_cocoa_generator::generate_cocoa_service_protocol(ofstream& out, t_service* tservice) { out << "@protocol " << cocoa_prefix_ << tservice->get_name() << " " << endl; vector functions = tservice->get_functions(); vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { out << "- " << function_signature(*f_iter) << ";" << " // throws "; t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { out << type_name((*x_iter)->get_type()) + ", "; } out << "TException" << endl; } out << "@end" << endl << endl; } /** * Generates a service client interface definition. * * @param tservice The service to generate a client interface definition for */ void t_cocoa_generator::generate_cocoa_service_client_interface(ofstream& out, t_service* tservice) { out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Client : NSObject <" << cocoa_prefix_ << tservice->get_name() << "> "; scope_up(out); out << indent() << "id inProtocol;" << endl; out << indent() << "id outProtocol;" << endl; scope_down(out); out << "- (id) initWithProtocol: (id ) protocol;" << endl; out << "- (id) initWithInProtocol: (id ) inProtocol outProtocol: (id ) outProtocol;" << endl; out << "@end" << endl << 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_cocoa_generator::generate_cocoa_service_server_interface(ofstream& out, t_service* tservice) { out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Processor : NSObject "; scope_up(out); out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() <<"> mService;" << endl; out << indent() << "NSDictionary * mMethodMap;" << endl; scope_down(out); out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service;" << endl; out << "- (id<"<get_name() << ">) service;" << endl; out << "@end" << endl << endl; } /** * Generates a service client implementation. * * @param tservice The service to generate an implementation for */ void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& out, t_service* tservice) { out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Client" << endl; // initializers out << "- (id) initWithProtocol: (id ) protocol" << endl; scope_up(out); out << indent() << "return [self initWithInProtocol: protocol outProtocol: protocol];" << endl; scope_down(out); out << endl; out << "- (id) initWithInProtocol: (id ) anInProtocol outProtocol: (id ) anOutProtocol" << endl; scope_up(out); out << indent() << "self = [super init];" << endl; out << indent() << "inProtocol = [anInProtocol retain_stub];" << endl; out << indent() << "outProtocol = [anOutProtocol retain_stub];" << endl; out << indent() << "return self;" << endl; scope_down(out); out << endl; // dealloc out << "- (void) dealloc" << endl; scope_up(out); out << indent() << "[inProtocol release_stub];" << endl; out << indent() << "[outProtocol release_stub];" << endl; out << indent() << "[super dealloc_stub];" << endl; scope_down(out); out << endl; // generate client method implementations vector functions = tservice->get_functions(); vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string funname = (*f_iter)->get_name(); 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(out) << "- " << function_signature(&send_function) << endl; scope_up(out); // Serialize the request out << indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" << " type: TMessageType_CALL" << " sequenceID: 0];" << endl; out << indent() << "[outProtocol writeStructBeginWithName: @\"" << argsname << "\"];" << endl; // write out function parameters t_struct* arg_struct = (*f_iter)->get_arglist(); const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { string fieldName = (*fld_iter)->get_name(); if (type_can_be_null((*fld_iter)->get_type())) { out << indent() << "if (" << fieldName << " != nil)"; scope_up(out); } out << indent() << "[outProtocol writeFieldBeginWithName: @\"" << fieldName << "\"" " type: " << type_to_enum((*fld_iter)->get_type()) << " fieldID: " << (*fld_iter)->get_key() << "];" << endl; generate_serialize_field(out, *fld_iter, fieldName); out << indent() << "[outProtocol writeFieldEnd];" << endl; if (type_can_be_null((*fld_iter)->get_type())) { scope_down(out); } } out << indent() << "[outProtocol writeFieldStop];" << endl; out << indent() << "[outProtocol writeStructEnd];" << endl; out << indent() << "[outProtocol writeMessageEnd];" << endl << indent() << "[[outProtocol transport] flush];" << endl; scope_down(out); out << endl; if (!(*f_iter)->is_oneway()) { 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(out) << "- " << function_signature(&recv_function) << endl; scope_up(out); // TODO(mcslee): Message validation here, was the seqid etc ok? // check for an exception out << indent() << "int msgType = 0;" << endl << indent() << "[inProtocol readMessageBeginReturningName: nil type: &msgType sequenceID: NULL];" << endl << indent() << "if (msgType == TMessageType_EXCEPTION) {" << endl << indent() << " TApplicationException * x = [TApplicationException read: inProtocol];" << endl << indent() << " [inProtocol readMessageEnd];" << endl << indent() << " @throw x;" << endl << indent() << "}" << endl; // FIXME - could optimize here to reduce creation of temporary objects. string resultname = function_result_helper_struct_type(*f_iter); out << indent() << cocoa_prefix_ << resultname << " * result = [[[" << cocoa_prefix_ << resultname << " alloc] init] autorelease_stub];" << endl; indent(out) << "[result read: inProtocol];" << endl; indent(out) << "[inProtocol readMessageEnd];" << endl; // Careful, only return _result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { out << indent() << "if ([result successIsSet]) {" << endl << indent() << " return [result success];" << endl << indent() << "}" << endl; } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { out << indent() << "if ([result " << (*x_iter)->get_name() << "IsSet]) {" << 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(out) << "return;" << endl; } else { out << indent() << "@throw [TApplicationException exceptionWithType: TApplicationException_MISSING_RESULT" << endl << indent() << " reason: @\"" << (*f_iter)->get_name() << " failed: unknown result\"];" << endl; } // Close function scope_down(out); out << endl; } // Open function indent(out) << "- " << function_signature(*f_iter) << endl; scope_up(out); indent(out) << "[self send_" << funname; // Declare the function arguments bool first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { string fieldName = (*fld_iter)->get_name(); out << " "; if (first) { first = false; out << ": " << fieldName; } else { out << fieldName << ": " << fieldName; } } out << "];" << endl; if (!(*f_iter)->is_oneway()) { out << indent(); if (!(*f_iter)->get_returntype()->is_void()) { out << "return "; } out << "[self recv_" << funname << "];" << endl; } scope_down(out); out << endl; } indent_down(); out << "@end" << endl << endl; } /** * Generates a service server implementation. In other words the actual TProcessor implementation * for the service. * * @param tservice The service to generate an implementation for */ void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& out, t_service* tservice) { out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Processor" << endl; indent_up(); // initializer out << endl; out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service" << endl; scope_up(out); out << indent() << "self = [super init];" << endl; out << indent() << "if (!self) {" << endl; out << indent() << " return nil;" << endl; out << indent() << "}" << endl; out << indent() << "mService = [service retain_stub];" << endl; out << indent() << "mMethodMap = [[NSMutableDictionary dictionary] retain_stub];" << endl; // generate method map for routing incoming calls vector functions = tservice->get_functions(); vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string funname = (*f_iter)->get_name(); scope_up(out); out << indent() << "SEL s = @selector(process_" << funname << "_withSequenceID:inProtocol:outProtocol:);" << endl; out << indent() << "NSMethodSignature * sig = [self methodSignatureForSelector: s];" << endl; out << indent() << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl; out << indent() << "[invocation setSelector: s];" << endl; out << indent() << "[invocation retainArguments];" << endl; out << indent() << "[mMethodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl; scope_down(out); } out << indent() << "return self;" << endl; scope_down(out); // implementation of the 'service' method which returns the service associated with this // processor out << endl; out << indent() << "- (id<"<get_name() << ">) service" << endl; out << indent() << "{" << endl; out << indent() << " return [[mService retain_stub] autorelease_stub];" << endl; out << indent() << "}" << endl; // implementation of the TProcess method, which dispatches the incoming call using the method map out << endl; out << indent() << "- (BOOL) processOnInputProtocol: (id ) inProtocol" << endl; out << indent() << " outputProtocol: (id ) outProtocol" <get_functions(); for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { out << endl; string funname = (*f_iter)->get_name(); out << indent() << "- (void) process_" << funname << "_withSequenceID: (int32_t) seqID inProtocol: (id) inProtocol outProtocol: (id) outProtocol" << endl; scope_up(out); string argstype = cocoa_prefix_ + function_args_helper_struct_type(*f_iter); out << indent() << argstype << " * args = [[" << argstype << " alloc] init];" << endl; out << indent() << "[args read: inProtocol];" << endl; out << indent() << "[inProtocol readMessageEnd];" << endl; // prepare the result if not oneway if (!(*f_iter)->is_oneway()) { string resulttype = cocoa_prefix_ + function_result_helper_struct_type(*f_iter); out << indent() << resulttype << " * result = [[" << resulttype << " alloc] init];" << endl; } // make the call to the actual service object out << indent(); if (!(*f_iter)->get_returntype()->is_void()) { out << "[result setSuccess: "; } out << "[mService " << funname; // supplying arguments t_struct* arg_struct = (*f_iter)->get_arglist(); const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; bool first = true; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { string fieldName = (*fld_iter)->get_name(); if (first) { first = false; out << ": [args " << fieldName << "]"; } else { out << " " << fieldName << ": [args " << fieldName << "]"; } } out << "]"; if (!(*f_iter)->get_returntype()->is_void()) { out << "]"; } out << ";" << endl; // write out the result if not oneway if (!(*f_iter)->is_oneway()) { out << indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" << endl; out << indent() << " type: TMessageType_REPLY" << endl; out << indent() << " sequenceID: seqID];" << endl; out << indent() << "[result write: outProtocol];" << endl; out << indent() << "[outProtocol writeMessageEnd];" << endl; out << indent() << "[[outProtocol transport] flush];" << endl; out << indent() << "[result release_stub];" << endl; } out << indent() << "[args release_stub];" << endl; scope_down(out); } // dealloc out << endl; out << "- (void) dealloc" << endl; scope_up(out); out << indent() << "[mService release_stub];" << endl; out << indent() << "[mMethodMap release_stub];" << endl; out << indent() << "[super dealloc_stub];" << endl; scope_down(out); out << endl; indent_down(); out << "@end" << endl << endl; } /** * Deserializes a field of any type. * * @param tfield The field * @param fieldName The variable name for this field */ void t_cocoa_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string fieldName) { t_type* type = get_true_type(tfield->get_type()); if (type->is_void()) { throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + tfield->get_name(); } if (type->is_struct() || type->is_xception()) { generate_deserialize_struct(out, (t_struct*)type, fieldName); } else if (type->is_container()) { generate_deserialize_container(out, type, fieldName); } else if (type->is_base_type() || type->is_enum()) { indent(out) << type_name(type) << " " << fieldName << " = [inProtocol "; 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: " + tfield->get_name(); break; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { out << "readBinary];"; } else { out << "readString];"; } break; case t_base_type::TYPE_BOOL: out << "readBool];"; break; case t_base_type::TYPE_BYTE: 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 Objective-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()); } } /** * Generates an unserializer for a struct, allocates the struct and invokes read: */ void t_cocoa_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string fieldName) { indent(out) << type_name(tstruct) << fieldName << " = [[" << type_name(tstruct, true) << " alloc] init];" << endl; indent(out) << "[" << fieldName << " read: inProtocol];" << endl; } /** * Deserializes a container by reading its size and then iterating */ void t_cocoa_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string fieldName) { string size = tmp("_size"); indent(out) << "int " << size << ";" << endl; // Declare variables, read header if (ttype->is_map()) { indent(out) << "[inProtocol readMapBeginReturningKeyType: NULL valueType: NULL size: &" << size << "];" << endl; indent(out) << "NSMutableDictionary * " << fieldName << " = [[NSMutableDictionary alloc] initWithCapacity: " << size << "];" << endl; } else if (ttype->is_set()) { indent(out) << "[inProtocol readSetBeginReturningElementType: NULL size: &" << size << "];" << endl; indent(out) << "NSMutableSet * " << fieldName << " = [[NSMutableSet alloc] initWithCapacity: " << size << "];" << endl; } else if (ttype->is_list()) { indent(out) << "[inProtocol readListBeginReturningElementType: NULL size: &" << size << "];" << endl; indent(out) << "NSMutableArray * " << fieldName << " = [[NSMutableArray alloc] initWithCapacity: " << size << "];" << endl; } // FIXME - the code above does not verify that the element types of // the containers being read match the element types of the // containers we are reading into. Does that matter? // For loop iterates over elements string i = tmp("_i"); indent(out) << "int " << 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, fieldName); } else if (ttype->is_set()) { generate_deserialize_set_element(out, (t_set*)ttype, fieldName); } else if (ttype->is_list()) { generate_deserialize_list_element(out, (t_list*)ttype, fieldName); } scope_down(out); // Read container end if (ttype->is_map()) { indent(out) << "[inProtocol readMapEnd];" << endl; } else if (ttype->is_set()) { indent(out) << "[inProtocol readSetEnd];" << endl; } else if (ttype->is_list()) { indent(out) << "[inProtocol readListEnd];" << endl; } } /** * Take a variable of a given type and wrap it in code to make it * suitable for putting into a container, if necessary. Basically, * wrap scaler primitives in NSNumber objects. */ string t_cocoa_generator::containerize(t_type * ttype, string fieldName) { // FIXME - optimize here to avoid autorelease pool? ttype = get_true_type(ttype); if (ttype->is_enum()) { return "[NSNumber numberWithInt: " + fieldName + "]"; } 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 "can't containerize void"; case t_base_type::TYPE_BOOL: return "[NSNumber numberWithBool: " + fieldName + "]"; case t_base_type::TYPE_BYTE: return "[NSNumber numberWithUnsignedChar: " + fieldName + "]"; case t_base_type::TYPE_I16: return "[NSNumber numberWithShort: " + fieldName + "]"; case t_base_type::TYPE_I32: return "[NSNumber numberWithLong: " + fieldName + "]"; case t_base_type::TYPE_I64: return "[NSNumber numberWithLongLong: " + fieldName + "]"; case t_base_type::TYPE_DOUBLE: return "[NSNumber numberWithDouble: " + fieldName + "]"; default: break; } } // do nothing return fieldName; } /** * Generates code to deserialize a map element */ void t_cocoa_generator::generate_deserialize_map_element(ofstream& out, t_map* tmap, string fieldName) { string key = tmp("_key"); string val = tmp("_val"); t_type* keyType = tmap->get_key_type(); t_type* valType = tmap->get_val_type(); t_field fkey(keyType, key); t_field fval(valType, val); generate_deserialize_field(out, &fkey, key); generate_deserialize_field(out, &fval, val); indent(out) << "[" << fieldName << " setObject: " << containerize(valType, val) << " forKey: " << containerize(keyType, key) << "];" << endl; if (type_can_be_null(keyType)) { if (!(get_true_type(keyType)->is_string())) { indent(out) << "[" << containerize(keyType, key) << " release_stub];" << endl; } } if (type_can_be_null(valType)) { if (!(get_true_type(valType)->is_string())) { indent(out) << "[" << containerize(valType, val) << " release_stub];" << endl; } } } /** * Deserializes a set element */ void t_cocoa_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string fieldName) { string elem = tmp("_elem"); t_type* type = tset->get_elem_type(); t_field felem(type, elem); generate_deserialize_field(out, &felem, elem); indent(out) << "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl; if (type_can_be_null(type)) { // deserialized strings are autorelease, so don't release them if (!(get_true_type(type)->is_string())) { indent(out) << "[" << containerize(type, elem) << " release_stub];" << endl; } } } /** * Deserializes a list element */ void t_cocoa_generator::generate_deserialize_list_element(ofstream& out, t_list* tlist, string fieldName) { string elem = tmp("_elem"); t_type* type = tlist->get_elem_type(); t_field felem(type, elem); generate_deserialize_field(out, &felem, elem); indent(out) << "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl; if (type_can_be_null(type)) { if (!(get_true_type(type)->is_string())) { indent(out) << "[" << containerize(type, elem) << " release_stub];" << endl; } } } /** * Serializes a field of any type. * * @param tfield The field to serialize * @param fieldName Name to of the variable holding the field */ void t_cocoa_generator::generate_serialize_field(ofstream& out, t_field* tfield, string fieldName) { 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 (type->is_struct() || type->is_xception()) { generate_serialize_struct(out, (t_struct*)type, fieldName); } else if (type->is_container()) { generate_serialize_container(out, type, fieldName); } else if (type->is_base_type() || type->is_enum()) { indent(out) << "[outProtocol "; 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: " + fieldName; break; case t_base_type::TYPE_STRING: if (((t_base_type*)type)->is_binary()) { out << "writeBinary: " << fieldName << "];"; } else { out << "writeString: " << fieldName << "];"; } break; case t_base_type::TYPE_BOOL: out << "writeBool: " << fieldName << "];"; break; case t_base_type::TYPE_BYTE: out << "writeByte: " << fieldName << "];"; break; case t_base_type::TYPE_I16: out << "writeI16: " << fieldName << "];"; break; case t_base_type::TYPE_I32: out << "writeI32: " << fieldName << "];"; break; case t_base_type::TYPE_I64: out << "writeI64: " << fieldName << "];"; break; case t_base_type::TYPE_DOUBLE: out << "writeDouble: " << fieldName << "];"; 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: " << fieldName << "];"; } out << endl; } else { printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str()); } } /** * Serialize a struct. * * @param tstruct The struct to serialize * @param fieldName Name of variable holding struct */ void t_cocoa_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string fieldName) { (void) tstruct; out << indent() << "[" << fieldName << " write: outProtocol];" << endl; } /** * Serializes a container by writing its size then the elements. * * @param ttype The type of container * @param fieldName Name of variable holding container */ void t_cocoa_generator::generate_serialize_container(ofstream& out, t_type* ttype, string fieldName) { scope_up(out); if (ttype->is_map()) { indent(out) << "[outProtocol writeMapBeginWithKeyType: " << type_to_enum(((t_map*)ttype)->get_key_type()) << " valueType: " << type_to_enum(((t_map*)ttype)->get_val_type()) << " size: [" << fieldName << " count]];" << endl; } else if (ttype->is_set()) { indent(out) << "[outProtocol writeSetBeginWithElementType: " << type_to_enum(((t_set*)ttype)->get_elem_type()) << " size: [" << fieldName << " count]];" << endl; } else if (ttype->is_list()) { indent(out) << "[outProtocol writeListBeginWithElementType: " << type_to_enum(((t_list*)ttype)->get_elem_type()) << " size: [" << fieldName << " count]];" << endl; } string iter = tmp("_iter"); string key; if (ttype->is_map()) { key = tmp("key"); indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " keyEnumerator];" << endl; indent(out) << "id " << key << ";" << endl; indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl; } else if (ttype->is_set()) { key = tmp("obj"); indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " objectEnumerator];" << endl; indent(out) << "id " << key << ";" << endl; indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl; } else if (ttype->is_list()) { key = tmp("i"); indent(out) << "int " << key << ";" << endl; indent(out) << "for (" << key << " = 0; " << key << " < [" << fieldName << " count]; " << key << "++)" << endl; } scope_up(out); if (ttype->is_map()) { generate_serialize_map_element(out, (t_map*)ttype, key, fieldName); } else if (ttype->is_set()) { generate_serialize_set_element(out, (t_set*)ttype, key); } else if (ttype->is_list()) { generate_serialize_list_element(out, (t_list*)ttype, key, fieldName); } scope_down(out); if (ttype->is_map()) { indent(out) << "[outProtocol writeMapEnd];" << endl; } else if (ttype->is_set()) { indent(out) << "[outProtocol writeSetEnd];" << endl; } else if (ttype->is_list()) { indent(out) << "[outProtocol writeListEnd];" << endl; } scope_down(out); } /** * Given a field variable name, wrap it in code that converts it to a * primitive type, if necessary. */ string t_cocoa_generator::decontainerize(t_field * tfield, string fieldName) { t_type * ttype = get_true_type(tfield->get_type()); if (ttype->is_enum()) { return "[" + fieldName + " intValue]"; } 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 "can't decontainerize void"; case t_base_type::TYPE_BOOL: return "[" + fieldName + " boolValue]"; case t_base_type::TYPE_BYTE: return "[" + fieldName + " unsignedCharValue]"; case t_base_type::TYPE_I16: return "[" + fieldName + " shortValue]"; case t_base_type::TYPE_I32: return "[" + fieldName + " longValue]"; case t_base_type::TYPE_I64: return "[" + fieldName + " longLongValue]"; case t_base_type::TYPE_DOUBLE: return "[" + fieldName + " doubleValue]"; default: break; } } // do nothing return fieldName; } /** * Serializes the members of a map. */ void t_cocoa_generator::generate_serialize_map_element(ofstream& out, t_map* tmap, string key, string mapName) { t_field kfield(tmap->get_key_type(), key); generate_serialize_field(out, &kfield, decontainerize(&kfield, key)); t_field vfield(tmap->get_val_type(), "[" + mapName + " objectForKey: " + key + "]"); generate_serialize_field(out, &vfield, decontainerize(&vfield, vfield.get_name())); } /** * Serializes the members of a set. */ void t_cocoa_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string elementName) { t_field efield(tset->get_elem_type(), elementName); generate_serialize_field(out, &efield, decontainerize(&efield, elementName)); } /** * Serializes the members of a list. */ void t_cocoa_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string index, string listName) { t_field efield(tlist->get_elem_type(), "[" + listName + " objectAtIndex: " + index + "]"); generate_serialize_field(out, &efield, decontainerize(&efield, efield.get_name())); } /** * Returns an Objective-C name * * @param ttype The type * @param class_ref Do we want a Class reference istead of a type reference? * @return Java type name, i.e. HashMap */ string t_cocoa_generator::type_name(t_type* ttype, bool class_ref) { if (ttype->is_typedef()) { return cocoa_prefix_ + ttype->get_name(); } string result; 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()) { result = "NSMutableDictionary"; } else if (ttype->is_set()) { result = "NSMutableSet"; } else if (ttype->is_list()) { result = "NSMutableArray"; } else { // Check for prefix t_program* program = ttype->get_program(); if (program != NULL) { result = program->get_namespace("cocoa") + ttype->get_name(); } else { result = ttype->get_name(); } } if (!class_ref) { result += " *"; } return result; } /** * Returns the Objective-C type that corresponds to the thrift type. * * @param tbase The base type */ string t_cocoa_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 "NSData *"; } else { return "NSString *"; } case t_base_type::TYPE_BOOL: return "BOOL"; case t_base_type::TYPE_BYTE: return "uint8_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 objective-c name for base type " + t_base_type::t_base_name(tbase); } } /** * 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_cocoa_generator::print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool defval, bool is_property) { type = get_true_type(type); indent(out); if (type->is_base_type()) { string v2 = render_const_value(out, type, value); if (defval) out << type_name(type) << " "; out << name << " = " << v2 << ";" << endl << endl; } else if (type->is_enum()) { if (defval) out << type_name(type) << " "; out << name << " = " << render_const_value(out, type, value) << ";" << endl << endl; } else if (type->is_struct() || type->is_xception()) { const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::const_iterator v_iter; if (defval) out << type_name(type) << " "; if (defval || is_property) out << name << " = [[[" << type_name(type, true) << " alloc] init] autorelease_stub];" << endl; else out << name << " = [[" << type_name(type, true) << " alloc] init];" << endl; 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); std::string cap_name = capitalize(v_iter->first->get_string()); indent(out) << "[" << name << " set" << cap_name << ":" << val << "];" << 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& val = value->get_map(); map::const_iterator v_iter; if (defval) out << "NSMutableDictionary *"; if (defval || is_property) out << name << " = [[[NSMutableDictionary alloc] initWithCapacity:" << val.size() << "] autorelease_stub]; " << endl; else out << name << " = [[NSMutableDictionary alloc] initWithCapacity:" << val.size() << "]; " << endl; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string key = render_const_value(out, ktype, v_iter->first, true); string val = render_const_value(out, vtype, v_iter->second, true); indent(out) << "[" << name << " setObject:" << val << " forKey:" << key << "];" << endl; } out << endl; } else if (type->is_list()) { t_type* etype = ((t_list*)type)->get_elem_type(); const vector& val = value->get_list(); vector::const_iterator v_iter; if (defval) out << "NSMutableArray *"; if (defval || is_property) out << name << " = [[[NSMutableArray alloc] initWithCapacity:" << val.size() <<"] autorelease_stub];" << endl; else out << name << " = [[NSMutableArray alloc] initWithCapacity:" << val.size() <<"];" << endl; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string val = render_const_value(out, etype, *v_iter, true); indent(out) << "[" << name << " addObject:" << val << "];" << endl; } out << endl; } else if (type->is_set()) { t_type* etype = ((t_set*)type)->get_elem_type(); const vector& val = value->get_list(); vector::const_iterator v_iter; if (defval) out << "NSMutableSet *"; if (defval || is_property) out << name << " = [[[NSMutableSet alloc] initWithCapacity:" << val.size() << "] autorelease_stub];" << endl; else out << name << " = [[NSMutableSet alloc] initWithCapacity:" << val.size() << "];" << endl; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string val = render_const_value(out, etype, *v_iter, true); indent(out) << "[" << name << " addObject:" << val << "];" << endl; } out << endl; } else { throw "compiler error: no const of type " + type->get_name(); } } string t_cocoa_generator::render_const_value(ofstream& out, t_type* type, t_const_value* value, bool containerize_it) { 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: // We must handle binary constant but the syntax of IDL defines // nothing about binary constant. // if ((t_base_type*)type)->is_binary()) // // binary code render << "@\"" << get_escaped_string(value) << '"'; break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() > 0) ? "YES" : "NO"); break; case t_base_type::TYPE_BYTE: 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, false); render << t; } if (containerize_it) { return containerize(type, render.str()); } return render.str(); } #if 0 /** ORIGINAL * Spit out code that evaluates to the specified constant value. */ string t_cocoa_generator::render_const_value(string name, t_type* type, t_const_value* value, bool containerize_it) { 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) ? "YES" : "NO"); break; case t_base_type::TYPE_BYTE: 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 if (type->is_struct() || type->is_xception()) { const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::const_iterator v_iter; if (val.size() > 0) render << "[[" << type_name(type, true) << " alloc] initWith"; else render << "[[" << type_name(type, true) << " alloc] init"; bool first = true; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { // FIXME The generated code does not match with initWithXXX // initializer and causes compile error. // Try: test/DebugProtoTest.thrift and test/SmallTest.thrift 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) { render << capitalize(v_iter->first->get_string()); first = false; } else { render << " " << v_iter->first->get_string(); } render << ": " << render_const_value(name, field_type, v_iter->second); } render << "]"; } else if (type->is_map()) { render << "[[NSDictionary alloc] initWithObjectsAndKeys: "; t_type* ktype = ((t_map*)type)->get_key_type(); t_type* vtype = ((t_map*)type)->get_val_type(); const map& val = value->get_map(); map::const_iterator v_iter; bool first = true; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string key = render_const_value(name, ktype, v_iter->first, true); string val = render_const_value(name, vtype, v_iter->second, true); if (first) { first = false; } else { render << ", "; } render << val << ", " << key; } if (first) render << " nil]"; else render << ", nil]"; } else if (type->is_list()) { render << "[[NSArray alloc] initWithObjects: "; t_type * etype = ((t_list*)type)->get_elem_type(); const vector& val = value->get_list(); bool first = true; vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { if (first) { first = false; } else { render << ", "; } render << render_const_value(name, etype, *v_iter, true); } if (first) render << " nil]"; else render << ", nil]"; } else if (type->is_set()) { render << "[[NSSet alloc] initWithObjects: "; t_type * etype = ((t_set*)type)->get_elem_type(); const vector& val = value->get_list(); bool first = true; vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { if (first) { first = false; } else { render << ", "; } render << render_const_value(name, etype, *v_iter, true); } if (first) render << " nil]"; else render << ", nil]"; } else { throw "don't know how to render constant for type: " + type->get_name(); } if (containerize_it) { return containerize(type, render.str()); } return render.str(); } #endif /** * Declares a field. * * @param ttype The type */ string t_cocoa_generator::declare_field(t_field* tfield) { return type_name(tfield->get_type()) + " __" + tfield->get_name() + ";"; } /** * Declares an Objective-C 2.0 property. * * @param tfield The field to declare a property for */ string t_cocoa_generator::declare_property(t_field* tfield) { std::ostringstream render; render << "@property (nonatomic, "; if (type_can_be_null(tfield->get_type())) render << "retain, "; render << "getter=" << decapitalize(tfield->get_name()) << ", setter=set" << capitalize(tfield->get_name()) + ":) " << type_name(tfield->get_type()) << " " << tfield->get_name() << ";"; return render.str(); } /** * Renders a function signature * * @param tfunction Function definition * @return String of rendered function definition */ string t_cocoa_generator::function_signature(t_function* tfunction) { t_type* ttype = tfunction->get_returntype(); std::string result = "(" + type_name(ttype) + ") " + tfunction->get_name() + argument_list(tfunction->get_arglist()); return result; } /** * Renders a colon separated list of types and names, suitable for an * objective-c parameter list */ string t_cocoa_generator::argument_list(t_struct* tstruct) { string result = ""; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { string argPrefix = ""; if (first) { first = false; } else { argPrefix = (*f_iter)->get_name(); result += " "; } result += argPrefix + ": (" + type_name((*f_iter)->get_type()) + ") " + (*f_iter)->get_name(); } return result; } /** * Converts the parse type to an Objective-C enum string for the given type. */ string t_cocoa_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_BYTE: 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(); } /** * Returns a format string specifier for the supplied parse type. */ string t_cocoa_generator::format_string_for_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: throw "NO T_VOID CONSTRUCT"; case t_base_type::TYPE_STRING: return "\\\"%@\\\""; case t_base_type::TYPE_BOOL: return "%i"; case t_base_type::TYPE_BYTE: return "%i"; case t_base_type::TYPE_I16: return "%hi"; case t_base_type::TYPE_I32: return "%i"; case t_base_type::TYPE_I64: return "%qi"; case t_base_type::TYPE_DOUBLE: return "%f"; } } else if (type->is_enum()) { return "%i"; } else if (type->is_struct() || type->is_xception()) { return "%@"; } else if (type->is_map()) { return "%@"; } else if (type->is_set()) { return "%@"; } else if (type->is_list()) { return "%@"; } throw "INVALID TYPE IN format_string_for_type: " + type->get_name(); } /** * Generate a call to a field's setter. * * @param tfield Field the setter is being called on * @param fieldName Name of variable to pass to setter */ string t_cocoa_generator::call_field_setter(t_field* tfield, string fieldName) { return "[self set" + capitalize(tfield->get_name()) + ": " + fieldName + "];"; } THRIFT_REGISTER_GENERATOR(cocoa, "Cocoa", " log_unexpected: Log every time an unexpected field ID or type is encountered.\n" " validate_required:\n" " Throws exception if any required field is not set.\n" ) thrift-compiler_0.9.1/cpp/src/generate/t_st_generator.cc0000644000175000017500000007667612203157755024225 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include "platform.h" #include "t_oop_generator.h" #include "version.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& parsed_options, const std::string& option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; out_dir_base_ = "gen-st"; } /** * Init and close methods */ void init_generator(); void close_generator(); /** * Program-level generation functions */ void generate_typedef (t_typedef* ttypedef); void generate_enum (t_enum* tenum); void generate_const (t_const* tconst); void generate_struct (t_struct* tstruct); void generate_xception (t_struct* txception); void generate_service (t_service* tservice); 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::ofstream& out, t_struct* tstruct, bool is_exception); void generate_accessors (std::ofstream& 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::ofstream &out, std::string name); void st_method(std::ofstream &out, std::string cls, std::string name); void st_method(std::ofstream &out, std::string cls, std::string name, std::string category); void st_close_method(std::ofstream &out); void st_class_method(std::ofstream &out, std::string cls, std::string name); void st_class_method(std::ofstream &out, std::string cls, std::string name, std::string category); void st_setter(std::ofstream &out, std::string cls, std::string name, std::string type); void st_getter(std::ofstream &out, std::string cls, std::string name); void st_accessors(std::ofstream &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; std::ofstream 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 enums = program_->get_enums(); vector::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 (string::iterator iter = cat.begin(); iter != cat.end(); ++iter) { 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::ofstream &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::ofstream &out, string cls, string name) { st_method(out, cls, name, "as yet uncategorized"); } void t_st_generator::st_class_method(std::ofstream &out, string cls, string name) { st_method(out, cls + " class", name); } void t_st_generator::st_class_method(std::ofstream &out, string cls, string name, string category) { st_method(out, cls, name, category); } void t_st_generator::st_method(std::ofstream &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::ofstream &out) { out << "! !" << endl << endl; indent_down(); } void t_st_generator::st_setter(std::ofstream &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::ofstream &out, string cls, string name) { st_method(out, cls, name + ""); out << "^ " << name; st_close_method(out); } void t_st_generator::st_accessors(std::ofstream &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 constants = tenum->get_constants(); vector::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_BYTE: 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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::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& val = value->get_list(); vector::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::ofstream& out, t_struct* tstruct, bool is_exception = false) { const vector& members = tstruct->get_members(); vector::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::ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); vector::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& fields = tstruct->get_sorted_members(); vector::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& fields = tstruct->get_members(); vector::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_BYTE: 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& fields = arg_struct->get_members(); vector::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& fields = xs->get_members(); vector::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& fields = fn->get_arglist()->get_members(); vector::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 functions = tservice->get_functions(); vector::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& fields = tstruct->get_members(); vector::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_BYTE: 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", "") thrift-compiler_0.9.1/cpp/src/generate/t_c_glib_generator.cc0000644000175000017500000033671112203157755025003 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include "platform.h" #include "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 /* forward declarations */ string initial_caps_to_underscores(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 &parsed_options, const string &option_string) : t_oop_generator(program) { (void) parsed_options; (void) option_string; /* set the output directory */ this->out_dir_base_ = "gen-c_glib"; /* 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(); void close_generator(); /* generation functions */ void generate_typedef(t_typedef *ttypedef); void generate_enum(t_enum *tenum); void generate_consts(vector consts); void generate_struct(t_struct *tstruct); void generate_service(t_service *tservice); void generate_xception(t_struct *tstruct); private: /* file streams */ ofstream f_types_; ofstream f_types_impl_; ofstream f_header_; ofstream f_service_; /* namespace variables */ string nspace; string nspace_u; string nspace_uc; string nspace_lc; /* helper functions */ bool is_complex_type(t_type *ttype); string type_name(t_type* ttype, bool in_typedef=false, bool is_const=false); string base_type_name(t_base_type *type); string type_to_enum(t_type *type); string constant_value(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(ofstream &out, t_type *ttype, string &base_name); /* generation functions */ void generate_const_initializer(string name, t_type *type, t_const_value *value); void generate_service_client(t_service *tservice); void generate_service_server(t_service *tservice); void generate_object(t_struct *tstruct); void generate_struct_writer(ofstream &out, t_struct *tstruct, string this_name, string this_get="", bool is_function=true); void generate_struct_reader(ofstream &out, t_struct *tstruct, string this_name, string this_get="", bool is_function=true); void generate_serialize_field(ofstream &out, t_field *tfield, string prefix, string suffix, int error_ret); void generate_serialize_struct(ofstream &out, t_struct *tstruct, string prefix, int error_ret); void generate_serialize_container(ofstream &out, t_type *ttype, string prefix, int error_ret); void generate_serialize_map_element(ofstream &out, t_map *tmap, string key, string value, int error_ret); void generate_serialize_set_element(ofstream &out, t_set *tset, string element, int error_ret); void generate_serialize_list_element(ofstream &out, t_list *tlist, string list, string index, int error_ret); void generate_deserialize_field(ofstream &out, t_field *tfield, string prefix, string suffix, int error_ret, bool allocate=true); void generate_deserialize_struct(ofstream &out, t_struct *tstruct, string prefix, int error_ret, bool allocate=true); void generate_deserialize_container(ofstream &out, t_type *ttype, string prefix, int error_ret); void generate_deserialize_map_element(ofstream &out, t_map *tmap, string prefix, int error_ret); void generate_deserialize_set_element(ofstream &out, t_set *tset, string prefix, int error_ret); void generate_deserialize_list_element(ofstream &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 " << endl << "#include " << endl << "#include " << endl; /* include other thrift includes */ const vector &includes = program_->get_includes(); for (size_t i = 0; i < includes.size(); ++i) { f_types_ << "/* other thrift includes */" << endl << "#include \"" << this->nspace_lc << initial_caps_to_underscores(includes[i]->get_name()) << "_types.h\"" << endl; } f_types_ << endl; /* include custom headers */ const vector &c_includes = program_->get_c_includes(); f_types_ << "/* custom thrift includes */" << endl; for (size_t i = 0; i < c_includes.size(); ++i) { if (c_includes[i][0] == '<') { f_types_ << "#include " << c_includes[i] << endl; } else { f_types_ << "#include \"" << c_includes[i] << "\"" << endl; } } f_types_ << endl; // include the types file f_types_impl_ << endl << "#include \"" << this->nspace_lc << program_name_u << "_types.h\"" << endl << "#include " << 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 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 constants = tenum->get_constants(); vector::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(); if ((*c_iter)->has_value()) { f_types_ << " = " << (*c_iter)->get_value(); } } indent_down(); f_types_ << endl << "};" << endl << "typedef enum _" << this->nspace << name << " " << this->nspace << name << ";" << endl << endl; } /** * Generates Thrift constants in C code. */ void t_c_glib_generator::generate_consts (vector consts) { f_types_ << "/* constants */" << endl; f_types_impl_ << "/* constants */" << endl; vector::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(); f_types_ << indent() << "#define " << this->nspace_uc << name_uc << " " << constant_value (name_lc, type, value) << endl; generate_const_initializer (name_lc, type, value); } 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 \"" << 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 " << endl << "#include " << endl << "#include " << endl << "#include \"" << filename << ".h\"" << endl << endl; // 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 << " " << this->nspace_uc << name_uc << "_ERROR_CODE," << endl << "} " << 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() || (ttype->is_base_type() && (((t_base_type *) ttype)->get_base() == t_base_type::TYPE_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) { (void) in_typedef; if (ttype->is_base_type()) { string bname = base_type_name ((t_base_type *) 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()) { // TODO: investigate other implementations besides GPtrArray cname = "GPtrArray *"; 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"; case t_base_type::TYPE_BOOL: case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: case t_base_type::TYPE_DOUBLE: cname = "GArray *"; break; case t_base_type::TYPE_STRING: break; default: throw "compiler error: no array info for type"; } } } if (is_const) { return "const " + cname; } else { return cname; } } // check for a namespace string pname = this->nspace + ttype->get_name(); if (is_complex_type (ttype)) { pname += " *"; } if (is_const) { return "const " + pname; } else { return pname; } } /** * Maps a Thrift primitive to a C primitive. */ string t_c_glib_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 "GByteArray *"; } else { return "gchar *"; } case t_base_type::TYPE_BOOL: return "gboolean"; case t_base_type::TYPE_BYTE: 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 "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_BYTE: 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 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_BYTE: 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_name (type) << ")" << value->get_integer(); } else if (type->is_struct() || type->is_xception() || type->is_list() || type->is_set() || type->is_map()) { 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& fields = tstruct->get_members(); vector::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& fields = tstruct->get_members(); vector::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_BYTE: 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; } /** * Generates C code that initializes complex constants. */ void t_c_glib_generator::generate_const_initializer(string name, t_type *type, t_const_value *value) { 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); if (type->is_struct() || type->is_xception()) { const vector &fields = ((t_struct *) type)->get_members(); vector::const_iterator f_iter; const map &val = value->get_map(); map::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(); } } 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_ << "static " << this->nspace << type->get_name() << " *" << endl << this->nspace_lc << name_lc << "_constant (void)" << endl << "{" << endl << " static " << this->nspace << type->get_name() << " *constant = NULL;" << endl << " if (constant == NULL)" << endl << " {" << endl << " constant = g_object_new (" << this->nspace_uc << "TYPE_" << type_uc << ", NULL);" << endl << initializers.str() << endl << " }" << endl << " return constant;" << endl << "}" << endl << endl; } else if (type->is_list()) { string list_type = "GPtrArray *"; // TODO: This initialization should contain a free function for container string list_initializer = "g_ptr_array_new();"; string list_appender = "g_ptr_array_add"; bool list_variable = false; t_type* etype = ((t_list*)type)->get_elem_type(); const vector& val = value->get_list(); vector::const_iterator v_iter; ostringstream initializers; 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_BYTE: 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"; } } 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 << " " << list_appender << "(constant, " << fname << ");" << endl; } else { initializers << " " << list_appender << "(constant, " << constant_value (fname, (t_type *) etype, (*v_iter)) << ");" << endl; } } f_types_impl_ << "static " << list_type << endl << this->nspace_lc << name_lc << "_constant (void)" << endl << "{" << endl << " static " << list_type << " constant = NULL;" << endl << " if (constant == NULL)" << endl << " {" << endl << " constant = " << list_initializer << endl << initializers.str() << endl << " }" << endl << " return constant;" << endl << "}" << endl << endl; } else if (type->is_set()) { t_type *etype = ((t_set *) type)->get_elem_type(); const vector& val = value->get_list(); vector::const_iterator v_iter; ostringstream initializers; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string fname = tmp (name); generate_const_initializer (fname, etype, (*v_iter)); initializers << " " << type_name (etype) << " " << fname << " = " << constant_value (fname, (t_type *) etype, (*v_iter)) << ";" << endl << " g_hash_table_insert (constant, &" << fname << ", &" << fname << ");" << endl; } f_types_impl_ << "static GHashTable *" << endl << this->nspace_lc << name_lc << "_constant (void)" << endl << "{" << endl << " static GHashTable *constant = NULL;" << endl << " if (constant == NULL)" << endl << " {" << endl << // TODO: This initialization should contain a free function for elements " constant = g_hash_table_new (NULL, NULL);" << endl << initializers.str() << endl << " }" << endl << " return constant;" << endl << "}" << endl << 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 vector& val = value->get_list(); vector::const_iterator v_iter; ostringstream initializers; 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)); generate_const_initializer (vname, vtype, (*v_iter)); initializers << " " << type_name (ktype) << " " << kname << " = " << constant_value (kname, (t_type *) ktype, (*v_iter)) << ";" << endl << " " << type_name (vtype) << " " << vname << " = " << constant_value (vname, (t_type *) vtype, (*v_iter)) << ";" << endl << " g_hash_table_insert (constant, &" << fname << ", &" << fname << ");" << endl; } f_types_impl_ << "static GHashTable *" << endl << this->nspace_lc << name_lc << "_constant (void)" << endl << "{" << endl << " static GHashTable *constant = NULL;" << endl << " if (constant == NULL)" << endl << " {" << endl << // TODO: This initialization should contain a free function for elements " constant = g_hash_table_new (NULL, NULL);" << endl << initializers.str() << endl << " }" << endl << " return constant;" << endl << "}" << endl << endl; } } /** * 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); // 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 functions = tservice->get_functions(); vector::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 << " GObject parent;" << endl << endl << " ThriftProtocol *input_protocol;" << endl << " ThriftProtocol *output_protocol;" << endl << "};" << 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 << " GObjectClass 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& fields = arglist->get_members(); vector::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& xceptions = xlist->get_members(); vector::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 << " G_TYPE_OBJECT, " << 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 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_); // Serialize the request f_service_ << indent() << "gint32 cseqid = 0;" << endl << indent() << "ThriftProtocol * protocol = " << this->nspace_uc << service_name_uc << "_CLIENT (iface)->output_protocol;" << endl << endl << indent() << "if (thrift_protocol_write_message_begin (protocol, \"" << name << "\", T_CALL, 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_ << endl << indent() << "gint32 rseqid;" << endl << indent() << "gchar * fname;" << endl << indent() << "ThriftMessageType mtype;" << endl << indent() << "ThriftProtocol * protocol = " << this->nspace_uc << service_name_uc << "_CLIENT (iface)->input_protocol;" << endl << endl << indent() << "if (thrift_protocol_read_message_begin " << "(protocol, &fname, &mtype, &rseqid, error) < 0)" << endl << indent() << "{" << endl << indent() << " if (fname) g_free (fname);" << endl << indent() << " return FALSE;" << endl << indent() << "}" << endl << endl << indent() << "if (mtype == T_EXCEPTION) {" << endl << indent() << " if (fname) g_free (fname);" << endl << indent() << " ThriftApplicationException *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() << "} else if (mtype != T_REPLY) {" << endl << 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() << "} else if (strncmp (fname, \"" << name << "\", " << name.length() << ") != 0) {" << endl << 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() << "}" << endl << indent() << "if (fname) g_free (fname);" << endl << endl; t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::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 &fields = arg_struct->get_members(); vector::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& xceptions = (*f_iter)->get_xceptions()->get_members(); vector::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 << "{" << endl; 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_ << " iface->" << funname << " = " << this->nspace_lc << service_name_lc << "_client_" << funname << ";" << endl; } f_service_ << "}" << endl << 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 << "{" << endl << " client->input_protocol = NULL;" << endl << " client->output_protocol = NULL;" << endl << "}" << endl << 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 << " 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 << "}" << endl << endl; } /** * Generates C code that represents a Thrift service server. */ void t_c_glib_generator::generate_service_server (t_service *tservice) { (void) tservice; /* get some C friendly service names */ string service_name_u = initial_caps_to_underscores(service_name_); string service_name_uc = to_upper_case(service_name_u); // write the server object instance definition in the header. // TODO: implement after implement TServer and TProcessor } /** * 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); // 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::const_iterator m_iter; const vector &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 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 the instance init function f_types_impl_ << "static void " << endl << this->nspace_lc << name_u << "_instance_init (" << this->nspace << name << " * object)" << endl << "{" << endl; // satisfy compilers with -Wall turned on indent_up(); 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* t = get_true_type ((*m_iter)->get_type()); if (t->is_base_type()) { // only have init's for base types 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(); string type_name_uc = to_upper_case (initial_caps_to_underscores((*m_iter)->get_type()->get_name())); indent(f_types_impl_) << "object->" << name << " = g_object_new (" << this->nspace_uc << "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; 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()) { t_type *etype = ((t_set *) t)->get_elem_type(); init_function = generate_new_hash_from_type (etype, NULL); } else if (t->is_list()) { t_type *etype = ((t_list *) t)->get_elem_type(); init_function = generate_new_array_from_type (etype); } indent(f_types_impl_) << "object->" << name << " = " << init_function << endl; } /* 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_free"; 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_BYTE: 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_free"; break; case t_base_type::TYPE_STRING: break; default: throw "compiler error: no array info for type"; } } f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl; f_types_impl_ << indent() << "{" << endl; indent_up(); f_types_impl_ << indent() << destructor_function << " (tobject->" << name << ", TRUE);" << 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() << "g_free (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; f_types_impl_ << "static void " << endl << this->nspace_lc << name_u << "_class_init (ThriftStructClass * cls)" << endl << "{" << endl; indent_up(); f_types_impl_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl << endl << indent() << "gobject_class->finalize = " << this->nspace_lc << name_u << "_finalize;" << endl << indent() << "cls->read = " << this->nspace_lc << name_u << "_read;" << endl << indent() << "cls->write = " << this->nspace_lc << name_u << "_write;" << endl; indent_down(); f_types_impl_ << "}" << endl << 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 (ofstream &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 &fields = tstruct->get_members(); vector ::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(ofstream &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 &fields = tstruct->get_members(); vector ::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(ofstream &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_BYTE: 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 (((t_base_type *) type)->is_binary()) { out << "binary (protocol, ((GByteArray *) " << name << ")->data, ((GByteArray *) " << name << ")->len"; } 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 if (type->is_enum()) { out << "i32 (protocol, (gint32) " << name; } out << ", error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl; } else { printf ("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", name.c_str(), type_name (type).c_str()); } } void t_c_glib_generator::generate_serialize_struct(ofstream &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(ofstream &out, t_type *ttype, string prefix, int error_ret) { scope_up(out); if (ttype->is_map()) { string length = "g_hash_table_size ((GHashTable *) " + prefix + ")"; 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 = tkey->is_string() || !tkey->is_base_type() ? "" : "*"; string 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() << "if ((ret = thrift_protocol_write_map_begin (protocol, " << type_to_enum (tkey) << ", " << type_to_enum (tval) << ", (gint32) " << length << ", error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << endl << indent() << "GList *key_list = NULL, *iter = NULL;" << endl << indent() << tkey_name << tkey_ptr << " key;" << endl << indent() << tval_name << tval_ptr << " value;" << endl << indent() << "g_hash_table_foreach ((GHashTable *) " << prefix << ", thrift_hash_table_get_keys, &key_list);" << endl << indent() << tkey_name << tkey_ptr << " keys[g_list_length (key_list)];" << endl << indent() << "int i=0, key_count = g_list_length (key_list);" << endl << indent() << "for (iter = g_list_first (key_list); iter; iter = iter->next)" << endl << indent() << "{" << endl << indent() << " keys[i++] = (" << tkey_name << tkey_ptr << ") iter->data;" << endl << indent() << "}" << endl << indent() << "g_list_free (key_list);" << endl << endl << indent() << "for (i = 0; i < key_count; ++i)" << endl; scope_up(out); out << indent() << "key = keys[i];" << endl << indent() << "value = (" << tval_name << tval_ptr << ") g_hash_table_lookup (((GHashTable *) " << prefix << "), (gpointer) key);" << endl << endl; generate_serialize_map_element (out, (t_map *) ttype, tkey_ptr + " key", tval_ptr + " value", error_ret); scope_down(out); out << indent() << "if ((ret = thrift_protocol_write_map_end (protocol, error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl; } else if (ttype->is_set()) { string length = "g_hash_table_size ((GHashTable *) " + prefix + ")"; 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() << "if ((ret = thrift_protocol_write_set_begin (protocol, " << type_to_enum (telem) << ", (gint32) " << length << ", error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << indent() << "GList *key_list = NULL, *iter = NULL;" << endl << indent() << telem_name << telem_ptr << " elem;" << endl << indent() << "gpointer value;" << endl << indent() << "THRIFT_UNUSED_VAR (value);" << endl << endl << indent() << "g_hash_table_foreach ((GHashTable *) " << prefix << ", thrift_hash_table_get_keys, &key_list);" << endl << indent() << telem_name << telem_ptr << " keys[g_list_length (key_list)];" << endl << indent() << "int i=0, key_count = g_list_length (key_list);" << endl << indent() << "for (iter = g_list_first (key_list); iter; iter = iter->next)" << endl << indent() << "{" << endl << indent() << " keys[i++] = (" << telem_name << telem_ptr << ") iter->data;" << endl << indent() << "}" << endl << indent() << "g_list_free (key_list);" << endl << endl << indent() << "for (i=0; iis_list()) { string length = prefix + "->len"; string i = tmp("i"); out << indent() << "if ((ret = thrift_protocol_write_list_begin (protocol, " << type_to_enum (((t_list *) ttype)->get_elem_type()) << ", (gint32) " << length << ", error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << indent() << "guint " << i << ";" << 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() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl; } scope_down(out); } void t_c_glib_generator::generate_serialize_map_element(ofstream &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(ofstream &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(ofstream &out, t_list *tlist, string list, string index, int error_ret) { t_type *ttype = tlist->get_elem_type(); // cast to non-const string cast = ""; string name = "g_ptr_array_index ((GPtrArray *) " + list + ", " + index + ")"; 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 array type"; break; case t_base_type::TYPE_BOOL: name = "g_array_index (" + list + ", gboolean, " + index + ")"; break; case t_base_type::TYPE_BYTE: name = "g_array_index (" + list + ", gint8, " + index + ")"; break; case t_base_type::TYPE_I16: name = "g_array_index (" + list + ", gint16, " + index + ")"; break; case t_base_type::TYPE_I32: name = "g_array_index (" + list + ", gint32, " + index + ")"; break; case t_base_type::TYPE_I64: name = "g_array_index (" + list + ", gint64, " + index + ")"; break; case t_base_type::TYPE_DOUBLE: name = "g_array_index (" + list + ", gdouble, " + index + ")"; break; case t_base_type::TYPE_STRING: cast = "(gchar*)"; break; default: throw "compiler error: no array info for type"; } } else if (ttype->is_map() || ttype->is_set()) { cast = "(GHashTable*)"; } else if (ttype->is_list()) { t_type *base = ((t_list *)ttype)->get_elem_type(); if (base->is_base_type()) { switch (((t_base_type *) base)->get_base()) { 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_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: case t_base_type::TYPE_DOUBLE: cast = "(GArray*)"; break; case t_base_type::TYPE_STRING: cast = "(GPtrArray*)"; break; default: throw "Compiler error: no array info for type"; } } else { cast = "(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(ofstream &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 "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 (((t_base_type *) 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_BYTE: 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 && ((t_base_type *) 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 { printf ("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name (type).c_str()); } // 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 (tfield->get_req() != t_field::T_REQUIRED && prefix != "") { indent(out) << prefix << "__isset_" << tfield->get_name() << suffix << " = TRUE;" << endl; } else if (tfield->get_req() == t_field::T_REQUIRED && prefix != "") { indent(out) << "isset_" << tfield->get_name() << " = TRUE;" << endl; } } void t_c_glib_generator::generate_deserialize_struct(ofstream &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; } out << indent() << "return " << error_ret << ";" << endl; indent_down(); out << indent() << "}" << endl << indent() << "xfer += ret;" << endl; } void t_c_glib_generator::generate_deserialize_container (ofstream &out, t_type *ttype, string prefix, int error_ret) { scope_up(out); if (ttype->is_map()) { out << indent() << "guint32 size;" << 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() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << endl; // iterate over map elements out << indent() << "/* iterate through each of the map's fields */" << endl << indent() << "guint32 i;" << 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() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl; } else if (ttype->is_set()) { out << indent() << "guint32 size;" << endl << indent() << "ThriftType element_type;" << endl << indent() << "if ((ret = thrift_protocol_read_set_begin (protocol, &element_type, &size, error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << endl; // iterate over the elements out << indent() << "/* iterate through the set elements */" << endl << indent() << "guint32 i;" << 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() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << endl; } else if (ttype->is_list()) { out << indent() << "guint32 size;" << endl << indent() << "ThriftType element_type;" << endl << indent() << "if ((ret = thrift_protocol_read_list_begin (protocol, &element_type, &size, error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << endl; out << indent() << "/* iterate through list elements */" << endl << indent() << "guint32 i;" << 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); out << indent() << "if ((ret = thrift_protocol_read_list_end (protocol, error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl << endl; } scope_down(out); } void t_c_glib_generator::declare_local_variable(ofstream &out, t_type *ttype, string &name) { string tname = type_name (ttype); string ptr = ttype->is_string() || !ttype->is_base_type() ? "" : "*"; if (ttype->is_map()) { out << indent() << tname << ptr << " " << name << " = g_hash_table_new (NULL, NULL);" << endl; } else if (ttype->is_enum()) { out << indent() << tname << ptr << " " << name << ";" << endl; } else { out << indent() << tname << ptr << " " << name << (ptr != "" ? " = g_new (" + tname + ", 1)" : " = NULL") << ";" << endl; } } void t_c_glib_generator::generate_deserialize_map_element(ofstream &out, t_map *tmap, string prefix, int error_ret) { t_type *tkey = tmap->get_key_type(); t_type *tval = tmap->get_val_type(); string tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*"; string tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*"; string keyname = tmp("key"); string valname = tmp("val"); declare_local_variable(out, tkey, keyname); declare_local_variable(out, tval, valname); // 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) << "g_hash_table_insert ((GHashTable *)" << prefix << ", (gpointer) " << keyname << ", (gpointer) " << valname << ");" << endl; } void t_c_glib_generator::generate_deserialize_set_element(ofstream &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); t_field felem (telem, telem_ptr + elem); generate_deserialize_field (out, &felem, "", "", error_ret); indent(out) << "g_hash_table_insert ((GHashTable *) " << prefix << ", (gpointer) " << elem << ", (gpointer) 1);" << endl; } void t_c_glib_generator::generate_deserialize_list_element(ofstream &out, t_list *tlist, string prefix, string index, int error_ret) { (void) index; t_type *ttype = tlist->get_elem_type(); string elem = tmp ("_elem"); string telem_ptr = ttype->is_string() || !ttype->is_base_type() ? "" : "*"; declare_local_variable(out, ttype, elem); t_field felem (ttype, telem_ptr + elem); generate_deserialize_field (out, &felem, "", "", error_ret); indent(out); 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 array type"; case t_base_type::TYPE_STRING: out << "g_ptr_array_add (" << prefix << ", " << elem << ");" << endl; return; case t_base_type::TYPE_BOOL: case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: case t_base_type::TYPE_DOUBLE: out << "g_array_append_vals (" << prefix << ", " << elem << ", 1);" << endl; return; default: throw "compiler error: no array info for type"; } } 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_BYTE: 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 "NULL"; case t_base_type::TYPE_STRING: 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) g_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_BYTE: 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: case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: 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: case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: 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 "NULL"; } 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_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 array type"; break; case t_base_type::TYPE_BOOL: return "g_array_new (0, 1, sizeof (gboolean));"; case t_base_type::TYPE_BYTE: return "g_array_new (0, 1, sizeof (gint8));"; case t_base_type::TYPE_I16: return "g_array_new (0, 1, sizeof (gint16));"; case t_base_type::TYPE_I32: return "g_array_new (0, 1, sizeof (gint32));"; case t_base_type::TYPE_I64: return "g_array_new (0, 1, sizeof (gint64));"; case t_base_type::TYPE_DOUBLE: return "g_array_new (0, 1, sizeof (gdouble));"; case t_base_type::TYPE_STRING: return "g_ptr_array_new_with_free_func (g_free);"; default: throw "compiler error: no array info for type"; } } else if (ttype->is_enum()) { return "g_array_new (0, 1, sizeof (gint32));"; } else { string free_func = generate_free_func_from_type(ttype); return "g_ptr_array_new_with_free_func (" + free_func + ");"; } return "g_ptr_array_new();"; } /*************************************** * UTILITY FUNCTIONS * ***************************************/ /** * Upper case a string. Wraps boost's string utility. */ string to_upper_case(string name) { string s (name); std::transform (s.begin(), s.end(), s.begin(), ::toupper); return s; // return boost::to_upper_copy (name); } /** * Lower case a string. Wraps boost's string utility. */ string to_lower_case(string name) { string s (name); std::transform (s.begin(), s.end(), s.begin(), ::tolower); return s; // return boost::to_lower_copy (name); } /** * 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; } /* register this generator with the main program */ THRIFT_REGISTER_GENERATOR(c_glib, "C, using GLib", "") thrift-compiler_0.9.1/cpp/src/platform.h0000644000175000017500000000214112203157755021052 0ustar eevanseevans00000000000000/* * 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 MINGW #include #include #else #include #include #endif #if defined MINGW #define MKDIR(x) mkdir(x) #else #define MKDIR(x) mkdir(x, S_IRWXU | S_IRWXG | S_IRWXO) #endif thrift-compiler_0.9.1/cpp/src/thriftl.ll0000644000175000017500000003433612204153722021064 0ustar eevanseevans00000000000000/* * 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. */ #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-label" #include #include #include #include "main.h" #include "globals.h" #include "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. */ #include "thrifty.h" void thrift_reserved_keyword(char* keyword) { yyerror("Cannot use reserved language keyword: \"%s\"\n", keyword); exit(1); } 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 ("/*""*"*"*/") multicomm ("/*"[^*]"/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/") doctext ("/**"([^*/]|[^*]"/"|"*"[^/])*"*"*"*/") comment ("//"[^\n]*) unixcomment ("#"[^\n]*) symbol ([:;\,\{\}\(\)\=<>\[\]]) st_identifier ([a-zA-Z-](\.[a-zA-Z_0-9-]|[a-zA-Z_0-9-])*) literal_begin (['\"]) %% {whitespace} { /* do nothing */ } {sillycomm} { /* do nothing */ } {multicomm} { /* do nothing */ } {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" { return tok_cpp_namespace; } "cpp_include" { return tok_cpp_include; } "cpp_type" { return tok_cpp_type; } "java_package" { return tok_java_package; } "cocoa_prefix" { return tok_cocoa_prefix; } "csharp_namespace" { return tok_csharp_namespace; } "delphi_namespace" { return tok_delphi_namespace; } "php_namespace" { return tok_php_namespace; } "py_module" { return tok_py_module; } "perl_package" { return tok_perl_package; } "ruby_namespace" { return tok_ruby_namespace; } "smalltalk_category" { return tok_smalltalk_category; } "smalltalk_prefix" { return tok_smalltalk_prefix; } "xsd_all" { return tok_xsd_all; } "xsd_optional" { return tok_xsd_optional; } "xsd_nillable" { return tok_xsd_nillable; } "xsd_namespace" { return tok_xsd_namespace; } "xsd_attrs" { return tok_xsd_attrs; } "include" { return tok_include; } "void" { return tok_void; } "bool" { return tok_bool; } "byte" { return tok_byte; } "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; } "BEGIN" { thrift_reserved_keyword(yytext); } "END" { thrift_reserved_keyword(yytext); } "__CLASS__" { thrift_reserved_keyword(yytext); } "__DIR__" { thrift_reserved_keyword(yytext); } "__FILE__" { thrift_reserved_keyword(yytext); } "__FUNCTION__" { thrift_reserved_keyword(yytext); } "__LINE__" { thrift_reserved_keyword(yytext); } "__METHOD__" { thrift_reserved_keyword(yytext); } "__NAMESPACE__" { thrift_reserved_keyword(yytext); } "abstract" { thrift_reserved_keyword(yytext); } "alias" { thrift_reserved_keyword(yytext); } "and" { thrift_reserved_keyword(yytext); } "args" { thrift_reserved_keyword(yytext); } "as" { thrift_reserved_keyword(yytext); } "assert" { thrift_reserved_keyword(yytext); } "begin" { thrift_reserved_keyword(yytext); } "break" { thrift_reserved_keyword(yytext); } "case" { thrift_reserved_keyword(yytext); } "catch" { thrift_reserved_keyword(yytext); } "class" { thrift_reserved_keyword(yytext); } "clone" { thrift_reserved_keyword(yytext); } "continue" { thrift_reserved_keyword(yytext); } "declare" { thrift_reserved_keyword(yytext); } "def" { thrift_reserved_keyword(yytext); } "default" { thrift_reserved_keyword(yytext); } "del" { thrift_reserved_keyword(yytext); } "delete" { thrift_reserved_keyword(yytext); } "do" { thrift_reserved_keyword(yytext); } "dynamic" { thrift_reserved_keyword(yytext); } "elif" { thrift_reserved_keyword(yytext); } "else" { thrift_reserved_keyword(yytext); } "elseif" { thrift_reserved_keyword(yytext); } "elsif" { thrift_reserved_keyword(yytext); } "end" { thrift_reserved_keyword(yytext); } "enddeclare" { thrift_reserved_keyword(yytext); } "endfor" { thrift_reserved_keyword(yytext); } "endforeach" { thrift_reserved_keyword(yytext); } "endif" { thrift_reserved_keyword(yytext); } "endswitch" { thrift_reserved_keyword(yytext); } "endwhile" { thrift_reserved_keyword(yytext); } "ensure" { thrift_reserved_keyword(yytext); } "except" { thrift_reserved_keyword(yytext); } "exec" { thrift_reserved_keyword(yytext); } "finally" { thrift_reserved_keyword(yytext); } "float" { thrift_reserved_keyword(yytext); } "for" { thrift_reserved_keyword(yytext); } "foreach" { thrift_reserved_keyword(yytext); } "function" { thrift_reserved_keyword(yytext); } "global" { thrift_reserved_keyword(yytext); } "goto" { thrift_reserved_keyword(yytext); } "if" { thrift_reserved_keyword(yytext); } "implements" { thrift_reserved_keyword(yytext); } "import" { thrift_reserved_keyword(yytext); } "in" { thrift_reserved_keyword(yytext); } "inline" { thrift_reserved_keyword(yytext); } "instanceof" { thrift_reserved_keyword(yytext); } "interface" { thrift_reserved_keyword(yytext); } "is" { thrift_reserved_keyword(yytext); } "lambda" { thrift_reserved_keyword(yytext); } "module" { thrift_reserved_keyword(yytext); } "native" { thrift_reserved_keyword(yytext); } "new" { thrift_reserved_keyword(yytext); } "next" { thrift_reserved_keyword(yytext); } "nil" { thrift_reserved_keyword(yytext); } "not" { thrift_reserved_keyword(yytext); } "or" { thrift_reserved_keyword(yytext); } "pass" { thrift_reserved_keyword(yytext); } "public" { thrift_reserved_keyword(yytext); } "print" { thrift_reserved_keyword(yytext); } "private" { thrift_reserved_keyword(yytext); } "protected" { thrift_reserved_keyword(yytext); } "public" { thrift_reserved_keyword(yytext); } "raise" { thrift_reserved_keyword(yytext); } "redo" { thrift_reserved_keyword(yytext); } "rescue" { thrift_reserved_keyword(yytext); } "retry" { thrift_reserved_keyword(yytext); } "register" { thrift_reserved_keyword(yytext); } "return" { thrift_reserved_keyword(yytext); } "self" { thrift_reserved_keyword(yytext); } "sizeof" { thrift_reserved_keyword(yytext); } "static" { thrift_reserved_keyword(yytext); } "super" { thrift_reserved_keyword(yytext); } "switch" { thrift_reserved_keyword(yytext); } "synchronized" { thrift_reserved_keyword(yytext); } "then" { thrift_reserved_keyword(yytext); } "this" { thrift_reserved_keyword(yytext); } "throw" { thrift_reserved_keyword(yytext); } "transient" { thrift_reserved_keyword(yytext); } "try" { thrift_reserved_keyword(yytext); } "undef" { thrift_reserved_keyword(yytext); } "union" { thrift_reserved_keyword(yytext); } "unless" { thrift_reserved_keyword(yytext); } "unsigned" { thrift_reserved_keyword(yytext); } "until" { thrift_reserved_keyword(yytext); } "use" { thrift_reserved_keyword(yytext); } "var" { thrift_reserved_keyword(yytext); } "virtual" { thrift_reserved_keyword(yytext); } "volatile" { thrift_reserved_keyword(yytext); } "when" { thrift_reserved_keyword(yytext); } "while" { thrift_reserved_keyword(yytext); } "with" { thrift_reserved_keyword(yytext); } "xor" { thrift_reserved_keyword(yytext); } "yield" { thrift_reserved_keyword(yytext); } {intconstant} { errno = 0; yylval.iconst = strtoll(yytext, NULL, 10); if (errno == ERANGE) { integer_overflow(yytext); } return tok_int_constant; } {hexconstant} { errno = 0; yylval.iconst = strtoll(yytext+2, NULL, 16); if (errno == ERANGE) { integer_overflow(yytext); } return tok_int_constant; } {dubconstant} { yylval.dconst = atof(yytext); return tok_dub_constant; } {identifier} { yylval.id = strdup(yytext); return tok_identifier; } {st_identifier} { yylval.id = strdup(yytext); return tok_st_identifier; } {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); } } } } {doctext} { /* 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(yytext + 3); g_doctext[strlen(g_doctext) - 2] = '\0'; g_doctext = clean_up_doctext(g_doctext); g_doctext_lineno = yylineno; } } . { unexpected_token(yytext); } . { /* Catch-all to let us catch "*" in the parser. */ return (int) yytext[0]; } %% /* vim: filetype=lex */ thrift-compiler_0.9.1/cpp/src/parse/0000755000175000017500000000000012204170451020156 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/cpp/src/parse/t_map.h0000644000175000017500000000320512203157755021442 0ustar eevanseevans00000000000000/* * 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 "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 { return true; } virtual std::string get_fingerprint_material() const { return "map<" + key_type_->get_fingerprint_material() + "," + val_type_->get_fingerprint_material() + ">"; } virtual void generate_fingerprint() { t_type::generate_fingerprint(); key_type_->generate_fingerprint(); val_type_->generate_fingerprint(); } private: t_type* key_type_; t_type* val_type_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_enum_value.h0000644000175000017500000000322612203157755023030 0ustar eevanseevans00000000000000/* * 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 #include "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) : name_(name), has_value_(false), value_(0) {} t_enum_value(std::string name, int value) : name_(name), has_value_(true), value_(value) {} ~t_enum_value() {} const std::string& get_name() { return name_; } bool has_value() { return has_value_; } int get_value() { return value_; } void set_value(int val) { has_value_ = true; value_ = val; } std::map annotations_; private: std::string name_; bool has_value_; int value_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_scope.h0000644000175000017500000001465212203157755022006 0ustar eevanseevans00000000000000/* * 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 #include #include #include "t_type.h" #include "t_service.h" #include "t_const.h" #include "t_const_value.h" #include "t_base_type.h" #include "t_map.h" #include "t_list.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() {} void add_type(std::string name, t_type* type) { types_[name] = type; } t_type* get_type(std::string name) { return types_[name]; } void add_service(std::string name, t_service* service) { services_[name] = service; } t_service* get_service(std::string name) { return services_[name]; } 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]; } void print() { std::map::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& map = const_val->get_map(); std::map::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() || ttype->is_set()) { const std::vector& val = const_val->get_list(); std::vector::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_struct()) { t_struct* tstruct = (t_struct*)ttype; const std::map& map = const_val->get_map(); std::map::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 == NULL) { 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 == NULL) { 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_BYTE: 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& map = constant->get_value()->get_map(); std::map::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& val = constant->get_value()->get_list(); std::vector::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. t_enum* tenum = (t_enum*)ttype; t_enum_value* enum_value = tenum->get_constant_by_value(const_val->get_integer()); if (enum_value == NULL) { 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 types_; // Map of names to constants std::map constants_; // Map of names to services std::map services_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_container.h0000644000175000017500000000250012203157755022644 0ustar eevanseevans00000000000000/* * 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 "t_type.h" class t_container : public t_type { public: t_container() : cpp_name_(), has_cpp_name_(false) {} virtual ~t_container() {} void set_cpp_name(std::string cpp_name) { cpp_name_ = cpp_name; has_cpp_name_ = true; } bool has_cpp_name() { return has_cpp_name_; } std::string get_cpp_name() { return cpp_name_; } bool is_container() const { return true; } private: std::string cpp_name_; bool has_cpp_name_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_service.h0000644000175000017500000000362712203157755022335 0ustar eevanseevans00000000000000/* * 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 "t_function.h" #include 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_(NULL) {} bool is_service() const { return true; } void set_extends(t_service* extends) { extends_ = extends; } void add_function(t_function* func) { std::vector::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& get_functions() const { return functions_; } t_service* get_extends() { return extends_; } virtual std::string get_fingerprint_material() const { // Services should never be used in fingerprints. throw "BUG: Can't get fingerprint material for service."; } private: std::vector functions_; t_service* extends_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_const_value.h0000644000175000017500000001001312203157755023202 0ustar eevanseevans00000000000000/* * 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 "t_enum.h" #include #include #include #include /** * A const value is something parsed that could be a map, set, list, struct * or whatever. * */ class t_const_value { public: enum t_const_value_type { CV_INTEGER, CV_DOUBLE, CV_STRING, CV_MAP, CV_LIST, CV_IDENTIFIER }; t_const_value() {} t_const_value(int64_t val) { set_integer(val); } t_const_value(std::string val) { 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_ == NULL) { 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 == NULL) { 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& 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& 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 { return valType_; } private: std::map mapVal_; std::vector listVal_; std::string stringVal_; int64_t intVal_; double doubleVal_; std::string identifierVal_; t_enum* enum_; t_const_value_type valType_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_typedef.h0000644000175000017500000000355612203157755022336 0ustar eevanseevans00000000000000/* * 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 #include "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, std::string symbolic) : t_type(program, symbolic), type_(type), symbolic_(symbolic) {} ~t_typedef() {} t_type* get_type() const { return type_; } const std::string& get_symbolic() const { return symbolic_; } bool is_typedef() const { return true; } virtual std::string get_fingerprint_material() const { return type_->get_fingerprint_material(); } virtual void generate_fingerprint() { t_type::generate_fingerprint(); if (!type_->has_fingerprint()) { type_->generate_fingerprint(); } } private: t_type* type_; std::string symbolic_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_function.h0000644000175000017500000000477612203157755022530 0ustar eevanseevans00000000000000/* * 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 #include "t_type.h" #include "t_struct.h" #include "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), oneway_(oneway) { xceptions_ = new t_struct(NULL); 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), 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() {} 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 annotations_; private: t_type* returntype_; std::string name_; t_struct* arglist_; t_struct* xceptions_; bool oneway_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_doc.h0000644000175000017500000000223012203157755021427 0ustar eevanseevans00000000000000/* * 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 /** * Documentation stubs * */ class t_doc { public: t_doc() : has_doc_(false) {} void set_doc(const std::string& doc) { doc_ = doc; has_doc_ = true; } const std::string& get_doc() const { return doc_; } bool has_doc() { return has_doc_; } private: std::string doc_; bool has_doc_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_program.h0000644000175000017500000003020212203157755022331 0ustar eevanseevans00000000000000/* * 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 #include #include // For program_name() #include "main.h" #include "t_doc.h" #include "t_scope.h" #include "t_base_type.h" #include "t_typedef.h" #include "t_enum.h" #include "t_const.h" #include "t_struct.h" #include "t_service.h" #include "t_list.h" #include "t_map.h" #include "t_set.h" #include "generate/t_generator_registry.h" //#include "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(); } t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) { name_ = program_name(path); scope_ = new t_scope(); } ~t_program() { if(scope_) { delete scope_; scope_ = NULL; } } // 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 const 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& get_typedefs() const { return typedefs_; } const std::vector& get_enums() const { return enums_; } const std::vector& get_consts() const { return consts_; } const std::vector& get_structs() const { return structs_; } const std::vector& get_xceptions() const { return xceptions_; } const std::vector& get_objects() const { return objects_; } const std::vector& get_services() const { return services_; } // 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 const std::vector& 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(t_type * t) { int occurances = program_typename_count(this, t); for (std::vector::iterator it = includes_.begin(); it != includes_.end(); ++it) { occurances += program_typename_count(*it, t); } return 0 == occurances; } /** * 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(t_program * prog, t_type * t) { int occurances = 0; occurances += collection_typename_count(prog, prog->typedefs_, t); occurances += collection_typename_count(prog, prog->enums_, t); occurances += collection_typename_count(prog, prog->objects_, t); occurances += collection_typename_count(prog, prog->services_, t); return occurances; } /** * 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 int collection_typename_count(t_program * prog, T type_collection, t_type * t) { int occurances = 0; for (typename T::iterator it = type_collection.begin(); it != type_collection.end(); ++it) if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t)) ++occurances; return occurances; } /** * 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(t_program * prog, t_type * t) { //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 (std::map::iterator it = prog->namespaces_.begin(); it != prog->namespaces_.end(); ++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 (std::map::iterator it = t->get_program()->namespaces_.begin(); it != t->get_program()->namespaces_.end(); ++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_; } // Includes 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); } std::vector& get_includes() { return includes_; } 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 int 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); std::string sub_namespace; if(base_language == "smalltalk") { pwarning(1, "Namespace 'smalltalk' is deprecated. Use 'st' instead"); base_language = "st"; } 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::const_iterator iter; if ((iter = namespaces_.find(language)) != namespaces_.end() || (iter = namespaces_.find("*" )) != namespaces_.end()) { return iter->second; } return std::string(); } // Language specific namespace / packaging void add_cpp_include(std::string path) { cpp_includes_.push_back(path); } const std::vector& get_cpp_includes() { return cpp_includes_; } void add_c_include(std::string path) { c_includes_.push_back(path); } const std::vector& get_c_includes() { return c_includes_; } 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 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 typedefs_; std::vector enums_; std::vector consts_; std::vector objects_; std::vector structs_; std::vector xceptions_; std::vector services_; // Dynamic namespaces std::map namespaces_; // C++ extra includes std::vector cpp_includes_; // C extra includes std::vector c_includes_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_list.h0000644000175000017500000000266612203157755021652 0ustar eevanseevans00000000000000/* * 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 "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 { return true; } virtual std::string get_fingerprint_material() const { return "list<" + elem_type_->get_fingerprint_material() + ">"; } virtual void generate_fingerprint() { t_type::generate_fingerprint(); elem_type_->generate_fingerprint(); } private: t_type* elem_type_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_type.h0000644000175000017500000001060412203157755021647 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include "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: virtual ~t_type() {} 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_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_; } t_type* get_true_type(); // Return a string that uniquely identifies this type // from any other thrift type in the world, as far as // TDenseProtocol is concerned. // We don't cache this, which is a little sloppy, // but the compiler is so fast that it doesn't really matter. virtual std::string get_fingerprint_material() const = 0; // Fingerprint should change whenever (and only when) // the encoding via TDenseProtocol changes. static const int fingerprint_len = 16; // Call this before trying get_*_fingerprint(). virtual void generate_fingerprint(); bool has_fingerprint() const { for (int i = 0; i < fingerprint_len; i++) { if (fingerprint_[i] != 0) { return true; } } return false; } const uint8_t* get_binary_fingerprint() const { return fingerprint_; } std::string get_ascii_fingerprint() const { std::string rv; const uint8_t* fp = get_binary_fingerprint(); for (int i = 0; i < fingerprint_len; i++) { rv += byte_to_hex(fp[i]); } return rv; } // 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 annotations_; protected: t_type() : program_(NULL) { memset(fingerprint_, 0, sizeof(fingerprint_)); } t_type(t_program* program) : program_(program) { memset(fingerprint_, 0, sizeof(fingerprint_)); } t_type(t_program* program, std::string name) : program_(program), name_(name) { memset(fingerprint_, 0, sizeof(fingerprint_)); } t_type(std::string name) : program_(NULL), name_(name) { memset(fingerprint_, 0, sizeof(fingerprint_)); } t_program* program_; std::string name_; uint8_t fingerprint_[fingerprint_len]; }; /** * Placeholder struct for returning the key and value of an annotation * during parsing. */ struct t_annotation { std::string key; std::string val; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_base_type.h0000644000175000017500000000622412203157755022644 0ustar eevanseevans00000000000000/* * 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 #include "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_BYTE, 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 { return base_ == TYPE_VOID; } bool is_string() const { return base_ == TYPE_STRING; } bool is_bool() const { return base_ == TYPE_BOOL; } void set_string_list(bool val) { string_list_ = val; } bool is_string_list() const { return (base_ == TYPE_STRING) && string_list_; } void set_binary(bool val) { binary_ = val; } bool is_binary() const { return (base_ == TYPE_STRING) && binary_; } void set_string_enum(bool val) { string_enum_ = val; } bool is_string_enum() const { return base_ == TYPE_STRING && string_enum_; } void add_string_enum_val(std::string val) { string_enum_vals_.push_back(val); } const std::vector& get_string_enum_vals() const { return string_enum_vals_; } bool is_base_type() const { return true; } virtual std::string get_fingerprint_material() const { std::string rv = t_base_name(base_); if (rv == "(unknown)") { throw "BUG: Can't get fingerprint material for this base type."; } return rv; } 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_BYTE : return "byte"; 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 string_enum_vals_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_struct.h0000644000175000017500000001165012203157755022214 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include "t_type.h" #include "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 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) { 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())) { // unions can't have required fields if( field->get_req() == t_field::T_REQUIRED) { pwarning( 1, "Required field %s of union %s set to optional.\n", field->get_name().c_str(), 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() != NULL) { 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 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()) != NULL) { 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() { return members_; } const members_type& get_sorted_members() { return members_in_id_order_; } bool is_struct() const { return !is_xception_; } bool is_xception() const { return is_xception_; } bool is_union() const { return is_union_; } virtual std::string get_fingerprint_material() const { std::string rv = "{"; members_type::const_iterator m_iter; for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) { rv += (*m_iter)->get_fingerprint_material(); rv += ";"; } rv += "}"; return rv; } virtual void generate_fingerprint() { t_type::generate_fingerprint(); members_type::const_iterator m_iter; for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) { (*m_iter)->get_type()->generate_fingerprint(); } } t_field* get_field_by_name(std::string field_name) { 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 NULL; } 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 thrift-compiler_0.9.1/cpp/src/parse/t_const.h0000644000175000017500000000304012203157755022010 0ustar eevanseevans00000000000000/* * 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 "t_type.h" #include "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 thrift-compiler_0.9.1/cpp/src/parse/t_set.h0000644000175000017500000000265612203157755021471 0ustar eevanseevans00000000000000/* * 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 "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) {} t_type* get_elem_type() const { return elem_type_; } bool is_set() const { return true; } virtual std::string get_fingerprint_material() const { return "set<" + elem_type_->get_fingerprint_material() + ">"; } virtual void generate_fingerprint() { t_type::generate_fingerprint(); elem_type_->generate_fingerprint(); } private: t_type* elem_type_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_enum.h0000644000175000017500000000511112203157755021627 0ustar eevanseevans00000000000000/* * 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 "t_enum_value.h" #include /** * 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) { name_ = name; } void append(t_enum_value* constant) { constants_.push_back(constant); } const std::vector& get_constants() { return constants_; } t_enum_value* get_constant_by_name(const std::string name) { const std::vector& enum_values = get_constants(); std::vector::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 NULL; } t_enum_value* get_constant_by_value(int64_t value) { const std::vector& enum_values = get_constants(); std::vector::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 NULL; } bool is_enum() const { return true; } virtual std::string get_fingerprint_material() const { return "enum"; } void resolve_values() { const std::vector& enum_values = get_constants(); std::vector::const_iterator c_iter; int lastValue = -1; for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) { if (! (*c_iter)->has_value()) { (*c_iter)->set_value(++lastValue); } else { lastValue = (*c_iter)->get_value(); } } } private: std::vector constants_; }; #endif thrift-compiler_0.9.1/cpp/src/parse/t_field.h0000644000175000017500000000701012203157755021746 0ustar eevanseevans00000000000000/* * 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 #include #include "t_doc.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_(NULL), xsd_optional_(false), xsd_nillable_(false), xsd_attrs_(NULL) {} t_field(t_type* type, std::string name, int32_t key) : type_(type), name_(name), key_(key), req_(T_OPT_IN_REQ_OUT), value_(NULL), xsd_optional_(false), xsd_nillable_(false), xsd_attrs_(NULL) {} ~t_field() {} 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_; } 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_; } // This is not the same function as t_type::get_fingerprint_material, // but it does the same thing. std::string get_fingerprint_material() const { std::ostringstream keystm; keystm << key_; return keystm.str() + ":" + ((req_ == T_OPTIONAL) ? "opt-" : "") + type_->get_fingerprint_material(); } /** * 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 annotations_; 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_; }; /** * 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 { int64_t value; bool auto_assigned; }; #endif thrift-compiler_0.9.1/cpp/src/parse/parse.cc0000644000175000017500000000072212203157755021613 0ustar eevanseevans00000000000000#include "t_type.h" #include "t_typedef.h" #include "md5.h" void t_type::generate_fingerprint() { std::string material = get_fingerprint_material(); md5_state_t ctx; md5_init(&ctx); md5_append(&ctx, (md5_byte_t*)(material.data()), (int)material.size()); md5_finish(&ctx, (md5_byte_t*)fingerprint_); } t_type* t_type::get_true_type() { t_type* type = this; while (type->is_typedef()) { type = ((t_typedef*)type)->get_type(); } return type; } thrift-compiler_0.9.1/cpp/src/main.cc0000755000175000017500000010071212203157755020316 0ustar eevanseevans00000000000000/* * 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 #include #include #include #include #include #include #include #include #include #include #ifdef MINGW # include /* for GetFullPathName */ #endif // Careful: must include globals first for extern definitions #include "globals.h" #include "main.h" #include "parse/t_program.h" #include "parse/t_scope.h" #include "generate/t_generator.h" #include "version.h" using namespace std; /** * Global program tree */ t_program* g_program; /** * Global types */ 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_byte; t_type* g_type_i16; t_type* g_type_i32; t_type* g_type_i64; t_type* g_type_double; /** * 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 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 location of the last parsed doctext comment. */ int g_doctext_lineno; /** * 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; /** * MinGW 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 MINGW char buf[MAX_PATH]; char* basename; DWORD len = GetFullPathName(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 MINGW 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[PATH_MAX]; 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 sp = g_incl_searchpath; sp.insert(sp.begin(), g_curdir); // iterate through paths vector::iterator it; for (it = sp.begin(); it != sp.end(); it++) { string sfilename = *(it) + "/" + filename; // Realpath! char rp[PATH_MAX]; 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; } /** * 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 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::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)); strcpy(doctext, 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& typedefs = program->get_typedefs(); vector::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& enums = program->get_enums(); vector::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& consts = program->get_consts(); vector::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& structs = program->get_structs(); vector::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& xceptions = program->get_xceptions(); vector::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& services = program->get_services(); vector::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()); } } } /** * Call generate_fingerprint for every structure and enum. */ void generate_all_fingerprints(t_program* program) { const vector& structs = program->get_structs(); vector::const_iterator s_iter; for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) { t_struct* st = *s_iter; st->generate_fingerprint(); } const vector& xceptions = program->get_xceptions(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { t_struct* st = *x_iter; st->generate_fingerprint(); } const vector& enums = program->get_enums(); vector::const_iterator e_iter; for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) { t_enum* e = *e_iter; e->generate_fingerprint(); } g_type_void->generate_fingerprint(); // If you want to generate fingerprints for implicit structures, start here. /* const vector& services = program->get_services(); vector::const_iterator v_iter; for (v_iter = services.begin(); v_iter != services.end(); ++v_iter) { t_service* sv = *v_iter; } */ } /** * 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, "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_BYTE: 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& enum_values = ((t_enum*)type)->get_constants(); vector::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& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::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& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { validate_const_rec(name + "", k_type, v_iter->first); validate_const_rec(name + "", 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& val = value->get_list(); vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { validate_const_rec(name + "", e_type, *v_iter); } } } /** * 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& members = throws->get_members(); vector::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; } /** * 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 yyin = fopen(path.c_str(), "r"); if (yyin == 0) { failure("Could not open input file: \"%s\"", 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& includes = program->get_includes(); vector::iterator iter; for (iter = includes.begin(); iter != includes.end(); ++iter) { parse(*iter, program); } // 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; yyin = fopen(path.c_str(), "r"); if (yyin == 0) { failure("Could not open input file: \"%s\"", 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& generator_strings) { // Oooohh, recursive code generation, hot!! if (gen_recurse) { const vector& includes = program->get_includes(); for (size_t i = 0; i < includes.size(); ++i) { // Propogate output path from parent to child programs includes[i]->set_out_path(program->get_out_path(), program->is_out_path_absolute()); generate(includes[i], generator_strings); } } // Generate code! try { pverbose("Program: %s\n", program->get_path().c_str()); // Compute fingerprints. generate_all_fingerprints(program); if (dump_docs) { dump_docstrings(program); } vector::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()); } else { pverbose("Generating \"%s\"\n", iter->c_str()); generator->generate_program(); delete generator; } } } catch (string s) { printf("Error: %s\n", s.c_str()); } catch (const char* exc) { printf("Error: %s\n", exc); } } /** * 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 generator_strings; // 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(1); } 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.push_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.push_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 MINGW //strip out trailing \ on Windows int 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 { 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(1); } // You gotta generate something! if (generator_strings.empty()) { fprintf(stderr, "No output language(s) specified\n"); usage(); } // Real-pathify it char rp[PATH_MAX]; if (argv[i] == NULL) { fprintf(stderr, "Missing file name\n"); usage(); } 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); // Initialize global types 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_byte = new t_base_type("byte", t_base_type::TYPE_BYTE); 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); // 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); // 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. delete program; delete g_type_void; delete g_type_string; delete g_type_bool; delete g_type_byte; delete g_type_i16; delete g_type_i32; delete g_type_i64; delete g_type_double; // Finished return 0; } thrift-compiler_0.9.1/cpp/src/md5.c0000644000175000017500000003022212203157755017707 0ustar eevanseevans00000000000000/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } thrift-compiler_0.9.1/cpp/src/windows/0000755000175000017500000000000012204170451020536 5ustar eevanseevans00000000000000thrift-compiler_0.9.1/cpp/src/windows/version.h0000644000175000017500000000236312204167721022406 0ustar eevanseevans00000000000000/* * 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 cogoright 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 cogo 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_VERSION_H_ #define _THRIFT_WINDOWS_VERSION_H_ 1 #if defined(_MSC_VER) && (_MSC_VER > 1200) #pragma once #endif // _MSC_VER #ifndef _WIN32 #error "This is a Windows header only" #endif #define PATH_MAX MAX_PATH #define THRIFT_VERSION "0.9.1" #ifndef S_ISDIR #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #endif // _THRIFT_WINDOWS_VERSION_H_ thrift-compiler_0.9.1/cpp/src/windows/version.h.in0000644000175000017500000000237712203157755023025 0ustar eevanseevans00000000000000/* * 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 cogoright 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 cogo 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_VERSION_H_ #define _THRIFT_WINDOWS_VERSION_H_ 1 #if defined(_MSC_VER) && (_MSC_VER > 1200) #pragma once #endif // _MSC_VER #ifndef _WIN32 #error "This is a Windows header only" #endif #define PATH_MAX MAX_PATH #define THRIFT_VERSION "@PACKAGE_VERSION@" #ifndef S_ISDIR #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #endif // _THRIFT_WINDOWS_VERSION_H_ thrift-compiler_0.9.1/cpp/src/windows/config.h0000644000175000017500000000236212203157755022172 0ustar eevanseevans00000000000000/* * 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 cogoright 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 cogo 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 #include #include #define strtoll(begin_ptr, end_ptr, length) _strtoi64(begin_ptr, end_ptr, length) #define PRIu64 "I64d" #define PRIi64 "I64d" #pragma warning(disable:4996) #endif // _THRIFT_WINDOWS_CONFIG_H_ thrift-compiler_0.9.1/cpp/src/main.h0000644000175000017500000000427312203157755020162 0ustar eevanseevans00000000000000/* * 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 #include "parse/t_const.h" #include "parse/t_field.h" /** * Defined in the flex library */ int yylex(void); int yyparse(void); /** * Expected to be defined by Flex/Bison */ void yyerror(const char* fmt, ...); /** * 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, ...); /** * 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); /** * Flex utilities */ extern int yylineno; extern char yytext[]; extern FILE* yyin; #endif thrift-compiler_0.9.1/cpp/compiler.vcxproj.filters0000644000175000017500000001330312203157755023166 0ustar eevanseevans00000000000000 generate generate generate generate parse parse parse parse parse parse parse parse parse parse parse parse parse parse parse parse parse parse windows windows {ae9d0a15-57ae-4f01-87a4-81f790249b83} {5df016bb-591b-420a-a535-4330d9187fbf} {b5c626af-afa5-433c-8e10-ee734533cb68} generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate generate parse thrift-compiler_0.9.1/cpp/version.h.in0000644000175000017500000000005312203157755020531 0ustar eevanseevans00000000000000#define THRIFT_VERSION "@PACKAGE_VERSION@" thrift-compiler_0.9.1/cpp/Makefile.am0000644000175000017500000001022612204153757020324 0ustar eevanseevans00000000000000# # 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. # Override Automake rule that forces .hh extension am__yacc_c2h = sed -e s/cc$$/h/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ -e s/c++$$/h++/ -e s/c$$/h/ AM_YFLAGS = -d LIBS = BUILT_SOURCES = bin_PROGRAMS = thrift noinst_LIBRARIES = libparse.a thrift_OBJDIR = obj thrift_SOURCES = src/main.cc \ src/md5.c \ src/generate/t_generator.cc \ src/generate/t_generator_registry.h \ src/globals.h \ src/main.h \ src/platform.h \ src/md5.h \ src/parse/t_doc.h \ src/parse/t_type.h \ src/parse/t_base_type.h \ src/parse/t_enum.h \ src/parse/t_enum_value.h \ src/parse/t_typedef.h \ src/parse/t_container.h \ src/parse/t_list.h \ src/parse/t_set.h \ src/parse/t_map.h \ src/parse/t_struct.h \ src/parse/t_field.h \ src/parse/t_service.h \ src/parse/t_function.h \ src/parse/t_program.h \ src/parse/t_scope.h \ src/parse/t_const.h \ src/parse/t_const_value.h \ src/parse/parse.cc \ src/generate/t_generator.h \ src/generate/t_oop_generator.h \ src/generate/t_html_generator.h \ src/windows/config.h \ src/windows/version.h # Specific client generator source thrift_SOURCES += src/generate/t_c_glib_generator.cc \ src/generate/t_cpp_generator.cc \ src/generate/t_java_generator.cc \ src/generate/t_as3_generator.cc \ src/generate/t_csharp_generator.cc \ src/generate/t_py_generator.cc \ src/generate/t_rb_generator.cc \ src/generate/t_perl_generator.cc \ src/generate/t_php_generator.cc \ src/generate/t_erl_generator.cc \ src/generate/t_cocoa_generator.cc \ src/generate/t_st_generator.cc \ src/generate/t_ocaml_generator.cc \ src/generate/t_hs_generator.cc \ src/generate/t_xsd_generator.cc \ src/generate/t_html_generator.cc \ src/generate/t_js_generator.cc \ src/generate/t_javame_generator.cc \ src/generate/t_delphi_generator.cc \ src/generate/t_go_generator.cc \ src/generate/t_gv_generator.cc \ src/generate/t_d_generator.cc thrift_CPPFLAGS = -I$(srcdir)/src thrift_CXXFLAGS = -Wall thrift_LDADD = @LEXLIB@ libparse.a libparse_a_CPPFLAGS = -I$(srcdir)/src libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused libparse_a_SOURCES = src/thrifty.yy \ src/thriftl.ll WINDOWS_DIST = \ compiler.sln \ compiler.vcxproj \ compiler.vcxproj.filters \ README_Windows.txt EXTRA_DIST = \ $(WINDOWS_DIST) clean-local: $(RM) thriftl.cc thrifty.cc thrifty.h thrifty.hh version.h windows/version.h src/main.cc: version.h thrift-compiler_0.9.1/cpp/README_Windows.txt0000644000175000017500000000234312203157755021502 0ustar eevanseevans00000000000000Building the Thrift IDL compiler in Windows ------------------------------------------- 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. 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. Download flex & bison from http://jaisantonyk.wordpress.com/2008/03/16/lex-and-yaccbison-in-windows/ Download bison.simple in addition to bison.exe . This build of bison is easier to use than the one on sourceforge which has a myriad of dependencies. Place these binaries somewhere in the path. From a command prompt: > cd thrift/compiler/cpp > flex -osrc\thriftl.cc src\thriftl.ll In the generated thriftl.cc, comment out #include Place a copy of bison.simple in thrift/compiler/cpp > bison -y -o "src/thrifty.cc" --defines src/thrifty.yy > move src\thrifty.cc.hh src\thrifty.hh 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. thrift-compiler_0.9.1/cpp/compiler.sln0000644000175000017500000000153612203157755020625 0ustar eevanseevans00000000000000 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 thrift-compiler_0.9.1/NOTICE0000644000175000017500000000024712330037326016405 0ustar eevanseevans00000000000000Apache Thrift Copyright 2006-2010 The Apache Software Foundation. This product includes software developed at The Apache Software Foundation (http://www.apache.org/).thrift-compiler_0.9.1/Makefile.am0000644000175000017500000000225412330037326017535 0ustar eevanseevans00000000000000# # 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. # ACLOCAL_AMFLAGS = -I ./aclocal SUBDIRS = cpp dist-hook: find $(distdir) -type f \( -iname ".deps" -or -iname ".libs" -or -iname ".gitignore" \ -or -iname ".DS_Store" -or -iname "._*" \) | xargs rm -rf find $(distdir) -type d \( -iname ".svn" -or -iname ".git" \) | xargs rm -rf print-version: @echo $(VERSION) EXTRA_DIST = \ README.Thrift \ CONTRIBUTORS \ LICENSE \ DISCLAIMER \ NOTICE thrift-compiler_0.9.1/README.Thrift0000644000175000017500000001074012330037326017617 0ustar eevanseevans00000000000000Apache Thrift Last Modified: 2013-June-6 License ======= 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. Introduction ============ Thrift is a lightweight, language-independent software stack with an associated code generation mechanism for RPC. Thrift provides clean abstractions for data transport, data serialization, and application level processing. The code generation system takes a simple definition language as its input and generates code across programming languages that uses the abstracted stack to build interoperable RPC clients and servers. Thrift is specifically designed to support non-atomic version changes across client and server code. For more details on Thrift's design and implementation, take a gander at the Thrift whitepaper included in this distribution or at the README files in your particular subdirectory of interest. Hierarchy ========= thrift/ compiler/ Contains the Thrift compiler, implemented in C++. lib/ Contains the Thrift software library implementation, subdivided by language of implementation. cpp/ java/ php/ py/ rb/ test/ Contains sample Thrift files and test code across the target programming languages. tutorial/ Contains a basic tutorial that will teach you how to develop software using Thrift. Requirements ============ See http://wiki.apache.org/thrift/ThriftRequirements for an up-to-date list of build requirements. Resources ========= More information about Thrift can be obtained on the Thrift webpage at: http://thrift.apache.org Acknowledgments =============== Thrift was inspired by pillar, a lightweight RPC tool written by Adam D'Angelo, and also by Google's protocol buffers. Installation ============ If you are building from the first time out of the source repository, you will need to generate the configure scripts. (This is not necessary if you downloaded a tarball.) From the top directory, do: ./bootstrap.sh Once the configure scripts are generated, thrift can be configured. From the top directory, do: ./configure You may need to specify the location of the boost files explicitly. If you installed boost in /usr/local, you would run configure as follows: ./configure --with-boost=/usr/local Note that by default the thrift C++ library is typically built with debugging symbols included. If you want to customize these options you should use the CXXFLAGS option in configure, as such: ./configure CXXFLAGS='-g -O2' ./configure CFLAGS='-g -O2' ./configure CPPFLAGS='-DDEBUG_MY_FEATURE' Run ./configure --help to see other configuration options Please be aware that the Python library will ignore the --prefix option and just install wherever Python's distutils puts it (usually along the lines of /usr/lib/pythonX.Y/site-packages/). If you need to control where the Python modules are installed, set the PY_PREFIX variable. (DESTDIR is respected for Python and C++.) Make thrift: make From the top directory, become superuser and do: make install Note that some language packages must be installed manually using build tools better suited to those languages (at the time of this writing, this applies to Java, Ruby, PHP). Look for the README file in the lib// folder for more details on the installation of each language library package. Testing ======= There are a large number of client library tests that can all be run from the top-level directory. make -k check This will make all of the libraries (as necessary), and run through the unit tests defined in each of the client libraries. If a single language fails, the make check will continue on and provide a synopsis at the end. To run the cross-language test suite, please run: sh test/test.sh This will run a set of tests that use different language clients and servers.thrift-compiler_0.9.1/LICENSE0000644000175000017500000003264312330037326016513 0ustar eevanseevans00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -------------------------------------------------- SOFTWARE DISTRIBUTED WITH THRIFT: The Apache Thrift software includes a number of subcomponents with separate copyright notices and license terms. Your use of the source code for the these subcomponents is subject to the terms and conditions of the following licenses. -------------------------------------------------- Portions of the following files are licensed under the MIT License: lib/erl/src/Makefile.am Please see doc/otp-base-license.txt for the full terms of this license. -------------------------------------------------- For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components: # Copyright (c) 2007 Thomas Porschberg # # Copying and distribution of this file, with or without # modification, are permitted in any medium without royalty provided # the copyright notice and this notice are preserved. -------------------------------------------------- For the compiler/cpp/src/thrift/md5.[ch] components: /* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ --------------------------------------------------- For the lib/rb/setup.rb: Copyright (c) 2000-2005 Minero Aoki, lib/ocaml/OCamlMakefile and lib/ocaml/README-OCamlMakefile components: Copyright (C) 1999 - 2007 Markus Mottl Licensed under the terms of the GNU Lesser General Public License 2.1 (see doc/lgpl-2.1.txt for the full terms of this license)