THRIFT-2877 Generate hashCode using primitives and static utility methods
Client: Java
Author: Roshan George <roshan@arjie.com>

The TBaseHelper.hashCode methods are the Java 8 implementations of hashCode for
those types.

This closes #448
diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc
index 419a461..6a2b888 100644
--- a/compiler/cpp/src/generate/t_java_generator.cc
+++ b/compiler/cpp/src/generate/t_java_generator.cc
@@ -1882,9 +1882,12 @@
   scope_down(out);
   out << endl;
 
+  const int MUL = 8191; // HashCode multiplier
+  const int B_YES = 131071;
+  const int B_NO = 524287;
   out << indent() << "@Override" << endl << indent() << "public int hashCode() {" << endl;
   indent_up();
-  indent(out) << "List<Object> list = new ArrayList<Object>();" << endl;
+  indent(out) << "int hashCode = 1;" << endl;
 
   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
     out << endl;
@@ -1894,24 +1897,55 @@
     bool can_be_null = type_can_be_null(t);
     string name = (*m_iter)->get_name();
 
-    string present = "true";
-
     if (is_optional || can_be_null) {
-      present += " && (" + generate_isset_check(*m_iter) + ")";
+      indent(out) << "hashCode = hashCode * " << MUL << " + ((" << generate_isset_check(*m_iter)
+                  << ") ? " << B_YES << " : " << B_NO << ");" << endl;
     }
 
-    indent(out) << "boolean present_" << name << " = " << present << ";" << endl;
-    indent(out) << "list.add(present_" << name << ");" << endl;
-    indent(out) << "if (present_" << name << ")" << endl;
+    if (is_optional || can_be_null) {
+      indent(out) << "if (" + generate_isset_check(*m_iter) + ")" << endl;
+      indent_up();
+    }
+
     if (t->is_enum()) {
-      indent(out) << "  list.add(" << name << ".getValue());" << endl;
+      indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".getValue();" << endl;
+    } else if (t->is_base_type()) {
+      switch(((t_base_type*)t)->get_base()) {
+      case t_base_type::TYPE_STRING:
+        indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".hashCode();" << endl;
+        break;
+      case t_base_type::TYPE_BOOL:
+        indent(out) << "hashCode = hashCode * " << MUL << " + ((" << name << ") ? "
+                    << B_YES << " : " << B_NO << ");" << endl;
+        break;
+      case t_base_type::TYPE_I8:
+        indent(out) << "hashCode = hashCode * " << MUL << " + (int) (" << name << ");" << endl;
+        break;
+      case t_base_type::TYPE_I16:
+      case t_base_type::TYPE_I32:
+        indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ";" << endl;
+        break;
+      case t_base_type::TYPE_I64:
+      case t_base_type::TYPE_DOUBLE:
+        indent(out) << "hashCode = hashCode * " << MUL << " + org.apache.thrift.TBaseHelper.hashCode(" << name << ");" << endl;
+        break;
+      case t_base_type::TYPE_VOID:
+        throw std::logic_error("compiler error: a struct field cannot be void");
+      default:
+        throw std::logic_error("compiler error: the following base type has no hashcode generator: " +
+               t_base_type::t_base_name(((t_base_type*)t)->get_base()));
+      }
     } else {
-      indent(out) << "  list.add(" << name << ");" << endl;
+      indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".hashCode();" << endl;
+    }
+
+    if (is_optional || can_be_null) {
+      indent_down();
     }
   }
 
   out << endl;
-  indent(out) << "return list.hashCode();" << endl;
+  indent(out) << "return hashCode;" << endl;
   indent_down();
   indent(out) << "}" << endl << endl;
 }
diff --git a/lib/java/src/org/apache/thrift/TBaseHelper.java b/lib/java/src/org/apache/thrift/TBaseHelper.java
index 5517536..559df33 100644
--- a/lib/java/src/org/apache/thrift/TBaseHelper.java
+++ b/lib/java/src/org/apache/thrift/TBaseHelper.java
@@ -324,4 +324,14 @@
     System.arraycopy(orig, 0, copy, 0, orig.length);
     return copy;
   }
+
+  public static int hashCode(long value) {
+    int low = (int) value;
+    int high = (int) (value >>> 32);
+    return high * 127 + low;
+  }
+
+  public static int hashCode(double value) {
+    return hashCode(Double.doubleToRawLongBits(value));
+  }
 }