THRIFT-2897: Implement hash and isEqual: methods

Client: cocoa
Patch:  Jim Speth

This closes #330
diff --git a/compiler/cpp/src/generate/t_cocoa_generator.cc b/compiler/cpp/src/generate/t_cocoa_generator.cc
index 3ad6c31..3cb9127 100644
--- a/compiler/cpp/src/generate/t_cocoa_generator.cc
+++ b/compiler/cpp/src/generate/t_cocoa_generator.cc
@@ -105,6 +105,8 @@
   void generate_cocoa_struct_encode_with_coder_method(ofstream& out,
                                                       t_struct* tstruct,
                                                       bool is_exception);
+  void generate_cocoa_struct_hash_method(ofstream& out, t_struct* tstruct);
+  void generate_cocoa_struct_is_equal_method(ofstream& out, t_struct* tstruct);
   void generate_cocoa_struct_field_accessor_declarations(std::ofstream& out,
                                                          t_struct* tstruct,
                                                          bool is_exception);
@@ -661,6 +663,89 @@
 }
 
 /**
+ * Generate the hash method for this struct
+ */
+void t_cocoa_generator::generate_cocoa_struct_hash_method(ofstream& out, t_struct* tstruct) {
+  indent(out) << "- (NSUInteger) hash" << endl;
+  scope_up(out);
+  out << indent() << "NSUInteger hash = 17;" << endl;
+
+  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) {
+    t_type* t = get_true_type((*m_iter)->get_type());
+    out << indent() << "hash = (hash * 31) ^ __" << (*m_iter)->get_name()
+        << "_isset ? 2654435761 : 0;" << endl;
+    out << indent() << "if (__" << (*m_iter)->get_name() << "_isset)" << endl;
+    scope_up(out);
+    if (type_can_be_null(t)) {
+      out << indent() << "hash = (hash * 31) ^ [__" << (*m_iter)->get_name() << " hash];" << endl;
+    } else {
+      out << indent() << "hash = (hash * 31) ^ [@(__" << (*m_iter)->get_name() << ") hash];"
+          << endl;
+    }
+    scope_down(out);
+  }
+
+  out << indent() << "return hash;" << endl;
+  scope_down(out);
+  out << endl;
+}
+
+/**
+ * Generate the isEqual method for this struct
+ */
+void t_cocoa_generator::generate_cocoa_struct_is_equal_method(ofstream& out, t_struct* tstruct) {
+  indent(out) << "- (BOOL) isEqual: (id) anObject" << endl;
+  scope_up(out);
+
+  indent(out) << "if (self == anObject) {" << endl;
+  indent_up();
+  indent(out) << "return YES;" << endl;
+  indent_down();
+  indent(out) << "}" << endl;
+
+  string class_name = cocoa_prefix_ + tstruct->get_name();
+
+  indent(out) << "if (![anObject isKindOfClass:[" << class_name << " class]]) {" << endl;
+  indent_up();
+  indent(out) << "return NO;" << endl;
+  indent_down();
+  indent(out) << "}" << endl;
+
+  indent(out) << class_name << " *other = (" << class_name << " *)anObject;" << endl;
+
+  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) {
+    t_type* t = get_true_type((*m_iter)->get_type());
+    string name = (*m_iter)->get_name();
+    if (type_can_be_null(t)) {
+      out << indent() << "if ((__" << name << "_isset != other->__" << name << "_isset) ||" << endl
+          << indent() << "    "
+          << "(__" << name << "_isset && "
+          << "((__" << name << " || other->__" << name << ") && "
+          << "![__" << name << " isEqual:other->__" << name << "]))) {" << endl;
+    } else {
+      out << indent() << "if ((__" << name << "_isset != other->__" << name << "_isset) ||" << endl
+          << indent() << "    "
+          << "(__" << name << "_isset && "
+          << "(__" << name << " != other->__" << name << "))) {" << endl;
+    }
+    indent_up();
+    indent(out) << "return NO;" << endl;
+    indent_down();
+    indent(out) << "}" << endl;
+  }
+
+  out << indent() << "return YES;" << endl;
+  scope_down(out);
+  out << endl;
+}
+
+/**
  * Generate struct implementation.
  *
  * @param tstruct      The struct definition
@@ -744,6 +829,9 @@
   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);
+  // hash and isEqual for NSObject
+  generate_cocoa_struct_hash_method(out, tstruct);
+  generate_cocoa_struct_is_equal_method(out, tstruct);
 
   // dealloc
   if (!members.empty()) {