Add -javabean option to Thrift

Summary: In case you want getters and setters and iterators and magic. Submitted by Dave Engberg

Reviewed By: dreiss


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665239 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc
index c138df3..90b9328 100644
--- a/compiler/cpp/src/generate/t_java_generator.cc
+++ b/compiler/cpp/src/generate/t_java_generator.cc
@@ -18,11 +18,12 @@
  */
 void t_java_generator::init_generator() {
   // Make output directory
-  mkdir(T_JAVA_DIR, S_IREAD | S_IWRITE | S_IEXEC);
+  const char* java_dir = bean_style_ ? T_JAVABEAN_DIR : T_JAVA_DIR;
+  mkdir(java_dir, S_IREAD | S_IWRITE | S_IEXEC);
   package_name_ = program_->get_java_package();
 
   string dir = package_name_;
-  string subdir = T_JAVA_DIR;
+  string subdir = java_dir;
   string::size_type loc;
   while ((loc = dir.find(".")) != string::npos) {
     subdir = subdir + "/" + dir.substr(0, loc);
@@ -362,12 +363,16 @@
   
   scope_up(out);
 
-  // Members are public
+  // Members are public for -java, private for -javabean
   const vector<t_field*>& members = tstruct->get_members();
   vector<t_field*>::const_iterator m_iter; 
   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
-    indent(out) <<
-      "public " << declare_field(*m_iter, false) << endl;
+    if (bean_style_) {
+      indent(out) << "private ";
+    } else {
+      indent(out) << "public ";
+    }
+    out << declare_field(*m_iter, false) << endl;
   }
 
   // Inner Isset class
@@ -400,7 +405,7 @@
   indent_down();
   indent(out) << "}" << endl << endl;
 
-  
+
   // Full constructor for all fields
   if (!members.empty()) {
     indent(out) <<
@@ -422,12 +427,15 @@
     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
       indent(out) << "this." << (*m_iter)->get_name() << " = " <<
         (*m_iter)->get_name() << ";" << endl;
+      indent(out) << "this.__isset." << (*m_iter)->get_name() << " = true;" << endl;
     }
     indent_down();
     indent(out) << "}" << endl << endl;
   }
 
-
+  if (bean_style_) {
+    generate_java_bean_boilerplate(out, tstruct);
+  }
   generate_java_struct_reader(out, tstruct);
   if (is_result) {
     generate_java_struct_result_writer(out, tstruct);
@@ -664,6 +672,113 @@
 }
 
 /**
+ * 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) {
+  const vector<t_field*>& fields = tstruct->get_members();
+  vector<t_field*>::const_iterator f_iter;
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    t_field* field = *f_iter;
+    t_type* type = get_true_type(field->get_type());
+    std::string field_name = field->get_name();
+    std::string cap_name = field_name;
+    cap_name[0] = toupper(cap_name[0]);
+
+    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 get" << cap_name << "() {" <<
+        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) << "public void addTo" << 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) <<
+        "();" << endl;
+      indent_down();
+      indent(out) << "}" << endl;
+      indent(out) << "this." << field_name << ".add(elem);" << endl;
+      indent(out) << "this.__isset." << field_name << " = true;" << 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) << "public void putTo" << 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(out) << "this.__isset." << field_name << " = true;" << endl;
+      indent_down();
+      indent(out) << "}" << endl << endl;
+    }
+
+    // Simple getter
+    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
+    indent(out) << "public void set" << cap_name << "(" << type_name(type) <<
+      " " << field_name << ") {" << endl;
+    indent_up();
+    indent(out) << "this." << field_name << " = " << field_name << ";" <<
+      endl;
+    indent(out) << "this.__isset." << field_name << " = true;" << endl;
+    indent_down();
+    indent(out) << "}" << endl << endl;
+
+    // Unsetter
+    indent(out) << "public void unset" << cap_name << "() {" << endl;
+    indent_up();
+    if (type->is_container() || type->is_struct() || type->is_xception()) {
+      indent(out) << "this." << field_name << " = null;" << endl;
+    }
+    indent(out) << "this.__isset." << field_name << " = false;" << endl;
+    indent_down();
+    indent(out) << "}" << endl << endl;
+  }
+}
+
+/**
  * Generates a toString() method for the given struct
  *
  * @param tstruct The struct definition
diff --git a/compiler/cpp/src/generate/t_java_generator.h b/compiler/cpp/src/generate/t_java_generator.h
index 9509d09..db08515 100644
--- a/compiler/cpp/src/generate/t_java_generator.h
+++ b/compiler/cpp/src/generate/t_java_generator.h
@@ -16,6 +16,7 @@
 
 // TODO(mcslee: Paramaterize the output dir
 #define T_JAVA_DIR "gen-java"
+#define T_JAVABEAN_DIR "gen-javabean"
 
 /**
  * Java code generator.
@@ -24,8 +25,9 @@
  */
 class t_java_generator : public t_oop_generator {
  public:
-  t_java_generator(t_program* program) :
-    t_oop_generator(program) {}
+  t_java_generator(t_program* program, bool bean_style=false) :
+    t_oop_generator(program),
+    bean_style_(bean_style) {}
 
   /**
    * Init and close methods
@@ -60,6 +62,7 @@
   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_bean_boilerplate(std::ofstream& out, t_struct* tstruct);
 
   void generate_function_helpers(t_function* tfunction);
 
@@ -156,6 +159,9 @@
   std::string package_name_;
   std::ofstream f_service_;
   std::string package_dir_;
+
+  bool bean_style_;
+
 };
 
 #endif
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index b070b3b..e46fcea 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -131,6 +131,7 @@
  */
 bool gen_cpp = false;
 bool gen_java = false;
+bool gen_javabean = false;
 bool gen_rb = false;
 bool gen_py = false;
 bool gen_xsd = false;
@@ -542,6 +543,7 @@
   fprintf(stderr, "Options:\n");
   fprintf(stderr, "  -cpp        Generate C++ output files\n");
   fprintf(stderr, "  -java       Generate Java output files\n");
+  fprintf(stderr, "  -javabean   Generate Java bean-style output files\n");
   fprintf(stderr, "  -php        Generate PHP output files\n");
   fprintf(stderr, "  -phpi       Generate PHP inlined files\n");
   fprintf(stderr, "  -py         Generate Python output files\n");
@@ -773,7 +775,14 @@
 
     if (gen_java) {
       pverbose("Generating Java\n");
-      t_java_generator* java = new t_java_generator(program);
+      t_java_generator* java = new t_java_generator(program, false);
+      java->generate_program();
+      delete java;
+    }
+
+    if (gen_javabean) {
+      pverbose("Generating Java Beans\n");
+      t_java_generator* java = new t_java_generator(program, true);
       java->generate_program();
       delete java;
     }
@@ -891,6 +900,8 @@
         gen_recurse = true;
       } else if (strcmp(arg, "-cpp") == 0) {
         gen_cpp = true;
+      } else if (strcmp(arg, "-javabean") == 0) {
+        gen_javabean = true;
       } else if (strcmp(arg, "-java") == 0) {
         gen_java = true;
       } else if (strcmp(arg, "-php") == 0) {
@@ -933,7 +944,7 @@
   }
 
   // You gotta generate something!
-  if (!gen_cpp && !gen_java && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_ocaml && !gen_erl && !gen_hs) {
+  if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_ocaml && !gen_erl && !gen_hs) {
     fprintf(stderr, "!!! No output language(s) specified\n\n");
     usage();
   }