THRIFT-860. ocaml: copy method and reset method

The attached patch provides the class copying and reset-to-default values methods.

Patch: Lev Walkin

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@991774 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_ocaml_generator.cc b/compiler/cpp/src/generate/t_ocaml_generator.cc
index 4f0a020..e1875f4 100644
--- a/compiler/cpp/src/generate/t_ocaml_generator.cc
+++ b/compiler/cpp/src/generate/t_ocaml_generator.cc
@@ -68,6 +68,7 @@
   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
@@ -80,6 +81,8 @@
   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<t_field *>& members);
+  void generate_ocaml_member_copy(std::ofstream& out, t_field *member);
 
   /**
    * Service-level generation functions
@@ -527,6 +530,78 @@
   generate_ocaml_struct_sig(f_types_i_,tstruct,is_exception);
 }
 
+void t_ocaml_generator::generate_ocaml_method_copy(ofstream& out,
+                                                   const vector<t_field *>& members) {
+    vector<t_field*>::const_iterator m_iter;
+
+    /* Create a copy of the current object */
+    indent(out) << "method copy =" << endl;
+    indent_up(); indent_up();
+    indent(out) << "let _new = Oo.copy self in" << endl;
+    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter)
+        generate_ocaml_member_copy(out, *m_iter);
+
+    indent_down();
+    indent(out) << "_new" << endl;
+    indent_down();
+}
+
+string t_ocaml_generator::struct_member_copy_of(t_type *type, string what) {
+    if (type->is_struct() || type->is_xception()) {
+        return what + string ("#copy");
+    } if (type->is_map()) {
+        string copy_of_k = struct_member_copy_of(((t_map *)type)->get_key_type(), "k");
+        string copy_of_v = struct_member_copy_of(((t_map *)type)->get_val_type(), "v");
+
+        if(copy_of_k == "k" && copy_of_v == "v") {
+                return string ("(Hashtbl.copy ") + what + string(")");
+        } else {
+                return string ("((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v -> Hashtbl.add nh ")
+                    + copy_of_k + string(" ") + copy_of_v
+                    + string(") oh; nh) ")
+                    + what + ")";
+        }
+    } if (type->is_set()) {
+        string copy_of = struct_member_copy_of(((t_set *)type)->get_elem_type(), "k");
+
+        if(copy_of == "k") {
+                return string ("(Hashtbl.copy ") + what + string(")");
+        } else {
+                return string ("((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v -> Hashtbl.add nh ")
+                    + copy_of + string(" true")
+                    + string(") oh; nh) ")
+                    + what + ")";
+        }
+    } if (type->is_list()) {
+        string copy_of = struct_member_copy_of(((t_list *)type)->get_elem_type(), "x");
+        if(copy_of != "x") {
+            return string("(List.map (fun x -> ")
+                + copy_of + string (") ")
+                + what + string(")");
+        } else {
+            return what;
+        }
+    }
+    return what;
+}
+
+void t_ocaml_generator::generate_ocaml_member_copy(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.
  *
@@ -546,8 +621,10 @@
   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;
@@ -591,7 +668,20 @@
         indent(out) << "method get_" << mname << " = _" << mname << endl;
         indent(out) << "method grab_" << mname << " = match _"<<mname<<" with None->raise (Field_empty \""<<tname<<"."<<mname<<"\") | Some " << x <<" -> " << x << endl;
         indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- Some " << x << endl;
+        indent(out) << "method unset_" << mname << " = _" << mname << " <- None" << endl;
   }
+
+  indent(out) << "method reset_" << mname << " = _" << mname << " <- ";
+  if(val) {
+    if(struct_member_persistent(tmember))
+        out << render_const_value(tmember->get_type(), tmember->get_value()) << endl;
+    else
+        out << "Some " << render_const_value(tmember->get_type(), tmember->get_value()) << endl;
+  } else {
+    out << "None" << endl;
+  }
+
+
 }
 
 /**
@@ -671,8 +761,12 @@
       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 : " << tname << endl;
   indent(out) << "method write : Protocol.t -> unit" << endl;
   indent_down();
   indent(out) << "end" << endl;