Skip to content

Commit

Permalink
THRIFT-1489 Add support for WCF bindings (optionally) to C# compiler,…
Browse files Browse the repository at this point in the history
… allowing web service usage of Thrift generated code

Patch: Kieran Benton

changes by roger:
- use ServiceModel, DataContract only when wcf is enabled
- indent space vs tab
- remove issue on lib/cpp/README_WINDOWS
- add testStringMap on test/csharp/ThriftTest/TestServer.cs
- add build to test/csharp/ThriftTest/maketest.sh

git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1232578 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
bufferoverflow committed Jan 17, 2012
1 parent 36f7a72 commit f0e517d
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 17 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,9 @@
/test/rb/Makefile.in
/thrift.exe
/ylwrap
/compiler/cpp/Debug
*.suo
*.cache
*.user
*.ipch
*.sdf
177 changes: 164 additions & 13 deletions compiler/cpp/src/generate/t_csharp_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ class t_csharp_generator : public t_oop_generator
iter = parsed_options.find("async");
async_ctp_ = (iter != parsed_options.end());

iter = parsed_options.find("wcf");
wcf_ = (iter != parsed_options.end());
if (wcf_) {
wcf_namespace_ = iter->second;
}

out_dir_base_ = "gen-csharp";
}
void init_generator();
Expand All @@ -62,15 +68,16 @@ class t_csharp_generator : public t_oop_generator
void generate_struct (t_struct* tstruct);
void generate_xception (t_struct* txception);
void generate_service (t_service* tservice);
void generate_property(ofstream& out, t_field* tfield, bool isPublic);
void generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, std::string fieldPrefix = "");
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<t_const*> 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_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false);
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);
Expand All @@ -96,6 +103,11 @@ class t_csharp_generator : public t_oop_generator
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);

Expand All @@ -112,6 +124,7 @@ class t_csharp_generator : public t_oop_generator
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);

bool type_can_be_null(t_type* ttype) {
while (ttype->is_typedef()) {
Expand All @@ -128,7 +141,9 @@ class t_csharp_generator : public t_oop_generator
std::string namespace_name_;
std::ofstream f_service_;
std::string namespace_dir_;
bool async_ctp_;
bool async_ctp_;
bool wcf_;
std::string wcf_namespace_;
};


Expand Down Expand Up @@ -176,7 +191,9 @@ string t_csharp_generator::csharp_type_usings() {
"using System.IO;\n" +
(async_ctp_ ? "using System.Threading.Tasks;\n" : "") +
"using Thrift;\n" +
"using Thrift.Collections;\n";
"using Thrift.Collections;\n" +
(wcf_ ? "using System.ServiceModel;\n" : "") +
"using System.Runtime.Serialization;\n";
}

string t_csharp_generator::csharp_thrift_usings() {
Expand All @@ -200,13 +217,17 @@ void t_csharp_generator::generate_enum(t_enum* tenum) {

start_csharp_namespace(f_enum);

generate_csharp_doc(f_enum, tenum);

indent(f_enum) <<
"public enum " << tenum->get_name() << "\n";
scope_up(f_enum);

vector<t_enum_value*> constants = tenum->get_constants();
vector<t_enum_value*>::iterator c_iter;
for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
generate_csharp_doc(f_enum, *c_iter);

int value = (*c_iter)->get_value();
indent(f_enum) << (*c_iter)->get_name() << " = " << value << "," << endl;
}
Expand Down Expand Up @@ -239,6 +260,7 @@ void t_csharp_generator::generate_consts(std::vector<t_const*> consts) {
vector<t_const*>::iterator c_iter;
bool need_static_constructor = false;
for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
generate_csharp_doc(f_consts, (*c_iter));
if (print_const_value(f_consts, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false)) {
need_static_constructor = true;
}
Expand Down Expand Up @@ -403,7 +425,7 @@ void t_csharp_generator::generate_csharp_struct(t_struct* tstruct, bool is_excep
f_struct <<
autogen_comment() <<
csharp_type_usings() <<
csharp_thrift_usings();
csharp_thrift_usings() << endl;

generate_csharp_struct_definition(f_struct, tstruct, is_exception);

Expand All @@ -417,8 +439,14 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru
}

out << endl;

generate_csharp_doc(out, tstruct);

indent(out) << "#if !SILVERLIGHT" << endl;
indent(out) << "[Serializable]" << endl;
if (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
}
indent(out) << "#endif" << endl;
bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());

Expand All @@ -444,15 +472,20 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru
out << endl;

for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
generate_property(out, *m_iter, true);
generate_csharp_doc(out, *m_iter);
generate_property(out, *m_iter, true, true);
}

if (members.size() > 0) {
out <<
endl <<
indent() << "public Isset __isset;" << endl <<
indent() << "#if !SILVERLIGHT" << endl <<
indent() << "[Serializable]" << endl <<
indent() << "[Serializable]" << endl;
if (wcf_) {
indent(out) << "[DataContract]" << endl;
}
out <<
indent() << "#endif" << endl <<
indent() << "public struct Isset {" << endl;
indent_up();
Expand Down Expand Up @@ -491,12 +524,47 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru
scope_down(out);
out << endl;

// generate a corresponding WCF fault to wrap the exception
if(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) << "[DataContract]" << endl;
indent(out) << "#endif" << endl;
bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());

indent(out) << "public " << (is_final ? "sealed " : "") << "partial class " << tstruct->get_name() << "Fault" << endl;

scope_up(out);

const vector<t_field*>& members = tstruct->get_members();
vector<t_field*>::const_iterator m_iter;

// make private members with public Properties
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
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;
Expand Down Expand Up @@ -753,7 +821,7 @@ void t_csharp_generator::generate_service(t_service* tservice) {
f_service_ <<
autogen_comment() <<
csharp_type_usings() <<
csharp_thrift_usings();
csharp_thrift_usings() << endl;

start_csharp_namespace(f_service_);

Expand Down Expand Up @@ -782,13 +850,34 @@ void t_csharp_generator::generate_service_interface(t_service* tservice) {
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<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter)
{
generate_csharp_doc(f_service_, *f_iter);

// if we're using WCF, add the corresponding attributes
if (wcf_) {
indent(f_service_) <<
"[OperationContract]" << endl;

const std::vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
indent(f_service_) << "[FaultContract(typeof(" + type_name((*x_iter)->get_type(), false, false) + "Fault))]" << endl;
}
}

indent(f_service_) <<
function_signature(*f_iter) << ";" << endl;
indent(f_service_) << "#if SILVERLIGHT" << endl;
Expand Down Expand Up @@ -1664,10 +1753,13 @@ void t_csharp_generator::generate_serialize_list_element(ofstream& out, t_list*
generate_serialize_field(out, &efield, "");
}

void t_csharp_generator::generate_property(ofstream& out, t_field* tfield, bool isPublic) {
generate_csharp_property(out, tfield, isPublic, "_");
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, std::string fieldPrefix) {
void t_csharp_generator::generate_csharp_property(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset, std::string fieldPrefix) {
if(wcf_ && isPublic) {
indent(out) << "[DataMember]" << endl;
}
indent(out) << (isPublic ? "public " : "private ") << type_name(tfield->get_type())
<< " " << prop_name(tfield) << endl;
scope_up(out);
Expand All @@ -1677,7 +1769,9 @@ void t_csharp_generator::generate_csharp_property(ofstream& out, t_field* tfield
scope_down(out);
indent(out) << "set" << endl;
scope_up(out);
indent(out) << "__isset." << tfield->get_name() << " = true;" << endl;
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);
Expand Down Expand Up @@ -1870,7 +1964,64 @@ string t_csharp_generator::type_to_enum(t_type* type) {
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,
"/// <summary>\n",
"/// ", contents,
"/// </summary>\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() + "\n<seealso cref=\"" + get_enum_class_name(field->get_type()) + "\"/>";
generate_csharp_docstring_comment(out, combined_message);
} else {
generate_csharp_doc(out, (t_doc*)field);
}
}

void t_csharp_generator::generate_csharp_doc(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<t_field*>& fields = tfunction->get_arglist()->get_members();
vector<t_field*>::const_iterator p_iter;
for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
t_field* p = *p_iter;
ps << "\n<param name=\"" << p->get_name() << "\">";
if (p->has_doc()) {
std::string str = p->get_doc();
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); // remove the newlines that appear from the parser
ps << str;
}
ps << "</param>";
}
generate_docstring_comment(out,
"",
"/// ",
"<summary>\n" + tfunction->get_doc() + "</summary>" + ps.str(),
"");
}
}

std::string t_csharp_generator::get_enum_class_name(t_type* type) {
string package = "";
t_program* program = type->get_program();
if (program != NULL && program != program_) {
package = program->get_namespace("csharp") + ".";
}
return package + type->get_name();
}

THRIFT_REGISTER_GENERATOR(csharp, "C#",
" async: add AsyncCTP support.\n")
" async: Adds Async CTP support.\n"
" wcf: Adds bindings for WCF to generated classes.\n"
)

3 changes: 0 additions & 3 deletions lib/cpp/README_WINDOWS
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ libthriftnb
This library contains the Thrift nonblocking server, which uses libevent.
To link this library you will also need to link libevent.

You MUST apply this patch to make generated code compile.
https://issues.apache.org/jira/browse/THRIFT-1139

Linking Against Thrift
======================

Expand Down
22 changes: 21 additions & 1 deletion test/csharp/ThriftTest/TestServer.cs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,27 @@ public Dictionary<int, int> testMap(Dictionary<int, int> thing)
}
Console.WriteLine("})");
return thing;
}
}

public Dictionary<string, string> testStringMap(Dictionary<string, string> thing)
{
Console.WriteLine("testStringMap({");
bool first = true;
foreach (string key in thing.Keys)
{
if (first)
{
first = false;
}
else
{
Console.WriteLine(", ");
}
Console.WriteLine(key + " => " + thing[key]);
}
Console.WriteLine("})");
return thing;
}

public THashSet<int> testSet(THashSet<int> thing)
{
Expand Down
6 changes: 6 additions & 0 deletions test/csharp/ThriftTest/maketest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@

../../../compiler/cpp/thrift --gen csharp -o . ../../ThriftTest.thrift
gmcs /t:library /out:./ThriftImpl.dll /recurse:./gen-csharp/* /reference:../../../lib/csharp/Thrift.dll
gmcs /out:TestClientServer.exe /reference:../../../lib/csharp/Thrift.dll /reference:ThriftImpl.dll TestClient.cs TestServer.cs Program.cs

export MONO_PATH=../../../lib/csharp/

timeout 120 ./TestClientServer.exe server &
./TestClientServer.exe client

0 comments on commit f0e517d

Please sign in to comment.