From 603d50437cbe9c76be942f863f2db9526459fce2 Mon Sep 17 00:00:00 2001 From: David Reiss Date: Tue, 2 Dec 2008 02:06:31 +0000 Subject: [PATCH] THRIFT-138. java: Create deep-copy constructors for Thrift structs - Create a copy constructor for every Thrift struct. The constructor performs a deep copy on the argument, resulting in no shared state. - Make thrift structions implement Cloneable and implement .clone() using the copy constructor. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@722332 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_java_generator.cc | 144 +++++++++++++++++- test/java/build.xml | 2 + test/java/src/DeepCopyTest.java | 114 ++++++++++++++ 3 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 test/java/src/DeepCopyTest.java diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc index 5606f81a..6da8e3c6 100644 --- a/compiler/cpp/src/generate/t_java_generator.cc +++ b/compiler/cpp/src/generate/t_java_generator.cc @@ -147,6 +147,8 @@ class t_java_generator : public t_oop_generator { void generate_java_doc (std::ofstream& out, t_doc* tdoc); + 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, t_type* type); /** * Helper rendering functions @@ -569,7 +571,7 @@ void t_java_generator::generate_java_struct_definition(ofstream &out, if (is_exception) { out << "extends Exception "; } - out << "implements TBase, java.io.Serializable "; + out << "implements TBase, java.io.Serializable, Cloneable "; scope_up(out); @@ -619,8 +621,8 @@ void t_java_generator::generate_java_struct_definition(ofstream &out, indent(out) << "}" << endl << endl; - // Full constructor for all fields if (!members.empty()) { + // Full constructor for all fields indent(out) << "public " << tstruct->get_name() << "(" << endl; indent_up(); @@ -652,6 +654,49 @@ void t_java_generator::generate_java_struct_definition(ofstream &out, 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(); + + 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(); + + indent(out) << "__isset." << field_name << " = other.__isset." << field_name << ";" << endl; + + if (type_can_be_null(type)) { + indent(out) << "if (other." << field_name << " != null) {" << 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, type); + out << ";" << endl; + } + + if (type_can_be_null(type)) { + 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() << " clone() {" << endl; + indent(out) << " return new " << tstruct->get_name() << "(this);" << endl; + indent(out) << "}" << endl << endl; + + if (bean_style_) { generate_java_bean_boilerplate(out, tstruct); generate_generic_field_getters_setters(out, tstruct); @@ -2543,6 +2588,101 @@ void t_java_generator::generate_java_doc(ofstream &out, } } +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; + + 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 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", 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", 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 + indent(out) << result_name << ".add("; + generate_deep_copy_non_container(out, iterator_element_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, t_type* type) { + if (type->is_base_type() || type->is_enum() || type->is_typedef()) { + out << source_name; + } else { + out << "new " << type_name(type, true, true) << "(" << source_name << ")"; + } +} + THRIFT_REGISTER_GENERATOR(java, "Java", " beans: Generate bean-style output files.\n" diff --git a/test/java/build.xml b/test/java/build.xml index 7b685fbc..1ed1ff1e 100644 --- a/test/java/build.xml +++ b/test/java/build.xml @@ -46,6 +46,8 @@ classpath="${cpath}:${testjar}:${gen}" failonerror="true" /> + diff --git a/test/java/src/DeepCopyTest.java b/test/java/src/DeepCopyTest.java new file mode 100644 index 00000000..a5b19cd1 --- /dev/null +++ b/test/java/src/DeepCopyTest.java @@ -0,0 +1,114 @@ + +package com.facebook.thrift.test; + +import com.facebook.thrift.TDeserializer; +import com.facebook.thrift.TSerializer; +import com.facebook.thrift.protocol.TBinaryProtocol; +import thrift.test.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +public class DeepCopyTest { + + private static final byte[] kUnicodeBytes = { + (byte)0xd3, (byte)0x80, (byte)0xe2, (byte)0x85, (byte)0xae, (byte)0xce, + (byte)0x9d, (byte)0x20, (byte)0xd0, (byte)0x9d, (byte)0xce, (byte)0xbf, + (byte)0xe2, (byte)0x85, (byte)0xbf, (byte)0xd0, (byte)0xbe, (byte)0xc9, + (byte)0xa1, (byte)0xd0, (byte)0xb3, (byte)0xd0, (byte)0xb0, (byte)0xcf, + (byte)0x81, (byte)0xe2, (byte)0x84, (byte)0x8e, (byte)0x20, (byte)0xce, + (byte)0x91, (byte)0x74, (byte)0x74, (byte)0xce, (byte)0xb1, (byte)0xe2, + (byte)0x85, (byte)0xbd, (byte)0xce, (byte)0xba, (byte)0x83, (byte)0xe2, + (byte)0x80, (byte)0xbc + }; + + public static void main(String[] args) throws Exception { + TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); + TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); + + OneOfEach ooe = new OneOfEach(); + ooe.im_true = true; + ooe.im_false = false; + ooe.a_bite = (byte) 0xd6; + ooe.integer16 = 27000; + ooe.integer32 = 1 << 24; + ooe.integer64 = (long) 6000 * 1000 * 1000; + ooe.double_precision = Math.PI; + ooe.some_characters = "JSON THIS! \"\1"; + ooe.zomg_unicode = new String(kUnicodeBytes, "UTF-8"); + + Nesting n = new Nesting(new Bonk(), new OneOfEach()); + n.my_ooe.integer16 = 16; + n.my_ooe.integer32 = 32; + n.my_ooe.integer64 = 64; + n.my_ooe.double_precision = (Math.sqrt(5) + 1) / 2; + n.my_ooe.some_characters = ":R (me going \"rrrr\")"; + n.my_ooe.zomg_unicode = new String(kUnicodeBytes, "UTF-8"); + n.my_bonk.type = 31337; + n.my_bonk.message = "I am a bonk... xor!"; + + HolyMoley hm = new HolyMoley(); + + hm.big = new ArrayList(); + hm.big.add(ooe); + hm.big.add(n.my_ooe); + hm.big.get(0).a_bite = (byte) 0x22; + hm.big.get(1).a_bite = (byte) 0x23; + + hm.contain = new HashSet>(); + ArrayList stage1 = new ArrayList(2); + stage1.add("and a one"); + stage1.add("and a two"); + hm.contain.add(stage1); + stage1 = new ArrayList(3); + stage1.add("then a one, two"); + stage1.add("three!"); + stage1.add("FOUR!!"); + hm.contain.add(stage1); + stage1 = new ArrayList(0); + hm.contain.add(stage1); + + ArrayList stage2 = new ArrayList(); + hm.bonks = new HashMap>(); + hm.bonks.put("nothing", stage2); + Bonk b = new Bonk(); + b.type = 1; + b.message = "Wait."; + stage2.add(b); + b = new Bonk(); + b.type = 2; + b.message = "What?"; + stage2.add(b); + stage2 = new ArrayList(); + hm.bonks.put("something", stage2); + b = new Bonk(); + b.type = 3; + b.message = "quoth"; + b = new Bonk(); + b.type = 4; + b.message = "the raven"; + b = new Bonk(); + b.type = 5; + b.message = "nevermore"; + hm.bonks.put("poe", stage2); + + + byte[] binaryCopy = binarySerializer.serialize(hm); + HolyMoley hmCopy = new HolyMoley(); + binaryDeserializer.deserialize(hmCopy, binaryCopy); + HolyMoley hmCopy2 = new HolyMoley(hm); + + if (!hm.equals(hmCopy)) + throw new RuntimeException("copy constructor modified the original object!"); + if (!hmCopy.equals(hmCopy2)) + throw new RuntimeException("copy constructor generated incorrect copy"); + + hmCopy2.bonks.get("nothing").get(1).message = "What else?"; + + if (hm.equals(hmCopy2)) + throw new RuntimeException("A deep copy was not done!"); + + //System.out.println("DeepCopyTest passed!"); + } +} -- 2.17.1