THRIFT-894. java: Make default accessors for binary fields return byte[]; provide new accessors to get ByteBuffer version

This patch causes the underlying ByteBuffer that backs a binary field to be hidden behind a default accessor that provides a byte[] interface. This should allow users who skipped 0.4 to update their generated code without breaking any of their other code. A new accessor has been added that allows a way down to the underlying ByteBuffer for those experts who want to take advantage.

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@996579 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 c1047f9..e136c33 100644
--- a/compiler/cpp/src/generate/t_java_generator.cc
+++ b/compiler/cpp/src/generate/t_java_generator.cc
@@ -1854,21 +1854,46 @@
 
     // Simple getter
     generate_java_doc(out, field);
-    indent(out) << "public " << type_name(type);
-    if (type->is_base_type() &&
-        ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) {
-      out << " is";
+    if (type->is_base_type() && ((t_base_type*)type)->is_binary()) {
+      indent(out) << "public byte[] get" << cap_name << "() {" << endl;
+      indent(out) << "  set" << cap_name << "(TBaseHelper.rightSize(" << field_name << "));" << endl;
+      indent(out) << "  return " << field_name << ".array();" << endl;
+      indent(out) << "}" << endl << endl;
+
+      indent(out) << "public ByteBuffer " << get_cap_name("bufferFor") << cap_name << "() {" << endl;
+      indent(out) << "  return " << field_name << ";" << endl;
+      indent(out) << "}" << endl << endl;
     } else {
-      out << " get";
+      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;
     }
-    out << cap_name << "() {" << endl;
-    indent_up();
-    indent(out) << "return this." << field_name << ";" << endl;
-    indent_down();
-    indent(out) << "}" << endl << endl;
 
     // Simple setter
     generate_java_doc(out, field);
+    if (type->is_base_type() && ((t_base_type*)type)->is_binary()) {
+      indent(out) << "public ";
+      if (bean_style_) {
+        out << "void";
+      } else {
+        out << type_name(tstruct);
+      }
+      out << " set" << cap_name << "(byte[] " << field_name << ") {" << endl;
+      indent(out) << "  set" << cap_name << "(ByteBuffer.wrap(" << field_name << "));" << endl;
+      if (!bean_style_) {
+        indent(out) << "  return this;" << endl;
+      }
+      indent(out) << "}" << endl << endl;
+    }
     indent(out) << "public ";
     if (bean_style_) {
       out << "void";
diff --git a/lib/java/src/org/apache/thrift/TBaseHelper.java b/lib/java/src/org/apache/thrift/TBaseHelper.java
index 9a7ee73..46075d3 100644
--- a/lib/java/src/org/apache/thrift/TBaseHelper.java
+++ b/lib/java/src/org/apache/thrift/TBaseHelper.java
@@ -240,4 +240,37 @@
     int extended = (b | 0x100) & 0x1ff;
     return Integer.toHexString(extended).toUpperCase().substring(1);
   }
+
+  public static byte[] byteBufferToByteArray(ByteBuffer byteBuffer) {
+    if (wrapsFullArray(byteBuffer)) {
+      return byteBuffer.array();
+    }
+    byte[] target = new byte[byteBuffer.remaining()];
+    byteBufferToByteArray(byteBuffer, target, 0);
+    return target;
+  }
+
+  public static boolean wrapsFullArray(ByteBuffer byteBuffer) {
+    return byteBuffer.hasArray()
+      && byteBuffer.position() == 0
+      && byteBuffer.arrayOffset() == 0
+      && byteBuffer.remaining() == byteBuffer.capacity();
+  }
+
+  public static int byteBufferToByteArray(ByteBuffer byteBuffer, byte[] target, int offset) {
+    int remaining = byteBuffer.remaining();
+    System.arraycopy(byteBuffer.array(),
+        byteBuffer.arrayOffset() + byteBuffer.position(),
+        target,
+        offset,
+        remaining);
+    return remaining;
+  }
+
+  public static ByteBuffer rightSize(ByteBuffer in) {
+    if (wrapsFullArray(in)) {
+      return in;
+    }
+    return ByteBuffer.wrap(byteBufferToByteArray(in));
+  }
 }
diff --git a/lib/java/test/org/apache/thrift/TestTBaseHelper.java b/lib/java/test/org/apache/thrift/TestTBaseHelper.java
index 6f24093..2c8caac 100644
--- a/lib/java/test/org/apache/thrift/TestTBaseHelper.java
+++ b/lib/java/test/org/apache/thrift/TestTBaseHelper.java
@@ -18,6 +18,7 @@
  */
 package org.apache.thrift;
 
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -139,4 +140,14 @@
     b.add(new byte[]{'a','b', 'a'});
     assertTrue(TBaseHelper.compareTo(a, b) > 0);
   }
+
+  public void testByteBufferToByteArray() throws Exception {
+    byte[] b1 = {10,9,8,7,6,5,4,3,2,1,0};
+    byte[] b2 = TBaseHelper.byteBufferToByteArray(ByteBuffer.wrap(b1));
+    assertEquals("b1 and b2 should be the exact same array (identity) due to fast path", b1, b2);
+
+    byte[] b3 = TBaseHelper.byteBufferToByteArray(ByteBuffer.wrap(b1, 1, 3));
+    assertEquals(3, b3.length);
+    assertEquals(ByteBuffer.wrap(b1, 1, 3), ByteBuffer.wrap(b3));
+  }
 }
diff --git a/lib/java/test/org/apache/thrift/TestTDeserializer.java b/lib/java/test/org/apache/thrift/TestTDeserializer.java
index 1f141bd..a4a353d 100644
--- a/lib/java/test/org/apache/thrift/TestTDeserializer.java
+++ b/lib/java/test/org/apache/thrift/TestTDeserializer.java
@@ -97,7 +97,7 @@
       String resultString = deserializer.partialDeserializeString(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.SOME_CHARACTERS);
       assertEquals(expectedString, resultString);
 
-      byte[] expectedBinary = level3OneOfEach.getBase64().array();
+      byte[] expectedBinary = level3OneOfEach.getBase64();
       ByteBuffer resultBinary = deserializer.partialDeserializeByteArray(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.BASE64);
       assertEquals(expectedBinary.length, resultBinary.limit() - resultBinary.position() - resultBinary.arrayOffset());
       assertEquals(ByteBuffer.wrap(expectedBinary), resultBinary);