Added convenient vector from array construction for Java.
authorWouter van Oortmerssen <wvo@google.com>
Thu, 11 Sep 2014 21:36:33 +0000 (14:36 -0700)
committerWouter van Oortmerssen <wvo@google.com>
Mon, 15 Sep 2014 23:19:27 +0000 (16:19 -0700)
Change-Id: Ib3fd576cf9fa4b4058a9fd1bbe24a0859bc3917a
Tested: on Linux.

docs/html/md__java_usage.html
docs/source/JavaUsage.md
src/idl_gen_java.cpp
tests/JavaTest.java
tests/MyGame/Example/Monster.java

index b3d353b..06f4678 100644 (file)
@@ -81,10 +81,11 @@ int mon = Monster.endMonster(fbb);
 <p>It's important to understand that fields that are structs are inline (like <code>Vec3</code> above), and MUST thus be created between the start and end calls of a table. Everything else (other tables, strings, vectors) MUST be created before the start of the table they are referenced in.</p>
 <p>Structs do have convenient methods that even have arguments for nested structs.</p>
 <p>As you can see, references to other objects (e.g. the string above) are simple ints, and thus do not have the type-safety of the Offset type in C++. Extra case must thus be taken that you set the right offset on the right field.</p>
-<p>Vectors also use this start/end pattern to allow vectors of both scalar types and structs: </p><pre class="fragment">Monster.startInventoryVector(fbb, 5);
+<p>Vectors can be created from the corresponding Java array like so: </p><pre class="fragment">int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
+</pre><p>This works for arrays of scalars and (int) offsets to strings/tables, but not structs. If you want to write structs, or what you want to write does not sit in an array, you can also use the start/end pattern: </p><pre class="fragment">Monster.startInventoryVector(fbb, 5);
 for (byte i = 4; i &gt;=0; i--) fbb.addByte(i);
 int inv = fbb.endVector();
-</pre><p>You can use the generated method <code>startInventoryVector</code> to conveniently call <code>startVector</code> with the right element size. You pass the number of elements you want to write. You write the elements backwards since the buffer is being constructed back to front.</p>
+</pre><p>You can use the generated method <code>startInventoryVector</code> to conveniently call <code>startVector</code> with the right element size. You pass the number of elements you want to write. Note how you write the elements backwards since the buffer is being constructed back to front.</p>
 <p>There are <code>add</code> functions for all the scalar types. You use <code>addOffset</code> for any previously constructed objects (such as other tables, strings, vectors). For structs, you use the appropriate <code>create</code> function in-line, as shown above in the <code>Monster</code> example.</p>
 <p>To finish the buffer, call: </p><pre class="fragment">Monster.finishMonsterBuffer(fbb, mon);
 </pre><p>The buffer is now ready to be transmitted. It is contained in the <code>ByteBuffer</code> which you can obtain from <code>fbb.dataBuffer()</code>. Importantly, the valid data does not start from offset 0 in this buffer, but from <code>fbb.dataBuffer().position()</code> (this is because the data was built backwards in memory). It ends at <code>fbb.capacity()</code>.</p>
index 0c1e2d5..2c8c10c 100755 (executable)
@@ -90,8 +90,13 @@ As you can see, references to other objects (e.g. the string above) are simple
 ints, and thus do not have the type-safety of the Offset type in C++. Extra
 case must thus be taken that you set the right offset on the right field.
 
-Vectors also use this start/end pattern to allow vectors of both scalar types
-and structs:
+Vectors can be created from the corresponding Java array like so:
+
+    int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
+
+This works for arrays of scalars and (int) offsets to strings/tables,
+but not structs. If you want to write structs, or what you want to write
+does not sit in an array, you can also use the start/end pattern:
 
     Monster.startInventoryVector(fbb, 5);
     for (byte i = 4; i >=0; i--) fbb.addByte(i);
@@ -99,8 +104,8 @@ and structs:
 
 You can use the generated method `startInventoryVector` to conveniently call
 `startVector` with the right element size. You pass the number of
-elements you want to write. You write the elements backwards since the buffer
-is being constructed back to front.
+elements you want to write. Note how you write the elements backwards since
+the buffer is being constructed back to front.
 
 There are `add` functions for all the scalar types. You use `addOffset` for
 any previously constructed objects (such as other tables, strings, vectors).
index 91847b5..7c83056 100755 (executable)
@@ -102,10 +102,10 @@ static std::string GenGetter(const Type &type) {
 }
 
 // Returns the method name for use with add/put calls.
-static std::string GenMethod(const FieldDef &field) {
-  return IsScalar(field.value.type.base_type)
-    ? MakeCamel(GenTypeBasic(field.value.type))
-    : (IsStruct(field.value.type) ? "Struct" : "Offset");
+static std::string GenMethod(const Type &type) {
+  return IsScalar(type.base_type)
+    ? MakeCamel(GenTypeBasic(type))
+    : (IsStruct(type) ? "Struct" : "Offset");
 }
 
 // Recursively generate arguments for a constructor, to deal with nested
@@ -148,7 +148,7 @@ static void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
       GenStructBody(*field.value.type.struct_def, code_ptr,
                     (field.value.type.struct_def->name + "_").c_str());
     } else {
-      code += "    builder.put" + GenMethod(field) + "(";
+      code += "    builder.put" + GenMethod(field.value.type) + "(";
       code += nameprefix + MakeCamel(field.name, false) + ");\n";
     }
   }
@@ -322,21 +322,33 @@ static void GenStruct(const Parser &parser, StructDef &struct_def,
       auto argname = MakeCamel(field.name, false);
       if (!IsScalar(field.value.type.base_type)) argname += "Offset";
       code += " " + argname + ") { builder.add";
-      code += GenMethod(field) + "(";
+      code += GenMethod(field.value.type) + "(";
       code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
       code += argname + ", " + field.value.constant;
       code += "); }\n";
       if (field.value.type.base_type == BASE_TYPE_VECTOR) {
-        code += "  public static void start" + MakeCamel(field.name);
-        code += "Vector(FlatBufferBuilder builder, int numElems) ";
-        code += "{ builder.startVector(";
         auto vector_type = field.value.type.VectorType();
         auto alignment = InlineAlignment(vector_type);
         auto elem_size = InlineSize(vector_type);
+        if (!IsStruct(vector_type)) {
+          // Generate a method to create a vector from a Java array.
+          code += "  public static int create" + MakeCamel(field.name);
+          code += "Vector(FlatBufferBuilder builder, ";
+          code += GenTypeBasic(vector_type) + "[] data) ";
+          code += "{ builder.startVector(";
+          code += NumToString(elem_size);
+          code += ", data.length, " + NumToString(alignment);
+          code += "); for (int i = data.length - 1; i >= 0; i--) builder.add";
+          code += GenMethod(vector_type);
+          code += "(data[i]); return builder.endVector(); }\n";
+        }
+        // Generate a method to start a vector, data to be added manually after.
+        code += "  public static void start" + MakeCamel(field.name);
+        code += "Vector(FlatBufferBuilder builder, int numElems) ";
+        code += "{ builder.startVector(";
         code += NumToString(elem_size);
         code += ", numElems, " + NumToString(alignment);
-        code += "); }\n";
-      }
+        code += "); }\n";      }
     }
     code += "  public static int end" + struct_def.name;
     code += "(FlatBufferBuilder builder) { return builder.endObject(); }\n";
index 74d68db..43467c3 100755 (executable)
@@ -52,12 +52,8 @@ class JavaTest {
         // We set up the same values as monsterdata.json:
 
         int str = fbb.createString("MyMonster");
-        int test1 = fbb.createString("test1");
-        int test2 = fbb.createString("test2");
 
-        Monster.startInventoryVector(fbb, 5);
-        for (byte i = 4; i >=0; i--) fbb.addByte(i);
-        int inv = fbb.endVector();
+        int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
 
         Monster.startMonster(fbb);
         Monster.addHp(fbb, (short)20);
@@ -68,10 +64,10 @@ class JavaTest {
         Test.createTest(fbb, (short)30, (byte)40);
         int test4 = fbb.endVector();
 
-        Monster.startTestarrayofstringVector(fbb, 2);
-        fbb.addOffset(test2);
-        fbb.addOffset(test1);
-        int testArrayOfString = fbb.endVector();
+        int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
+            fbb.createString("test1"),
+            fbb.createString("test2")
+        });
 
         Monster.startMonster(fbb);
         Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
@@ -93,10 +89,10 @@ class JavaTest {
         // Java code. They are functionally equivalent though.
 
         try {
-             DataOutputStream os = new DataOutputStream(new FileOutputStream(
+            DataOutputStream os = new DataOutputStream(new FileOutputStream(
                                            "monsterdata_java_wire.bin"));
-             os.write(fbb.dataBuffer().array(), fbb.dataBuffer().position(), fbb.offset());
-             os.close();
+            os.write(fbb.dataBuffer().array(), fbb.dataBuffer().position(), fbb.offset());
+            os.close();
         } catch(java.io.IOException e) {
             System.out.println("FlatBuffers test: couldn't write file");
             return;
index fa7bc21..f814898 100755 (executable)
@@ -50,6 +50,7 @@ public class Monster extends Table {
   public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); }
   public static void addName(FlatBufferBuilder builder, int nameOffset) { builder.addOffset(3, nameOffset, 0); }
   public static void addInventory(FlatBufferBuilder builder, int inventoryOffset) { builder.addOffset(5, inventoryOffset, 0); }
+  public static int createInventoryVector(FlatBufferBuilder builder, byte[] data) { builder.startVector(1, data.length, 1); for (int i = data.length - 1; i >= 0; i--) builder.addByte(data[i]); return builder.endVector(); }
   public static void startInventoryVector(FlatBufferBuilder builder, int numElems) { builder.startVector(1, numElems, 1); }
   public static void addColor(FlatBufferBuilder builder, byte color) { builder.addByte(6, color, 8); }
   public static void addTestType(FlatBufferBuilder builder, byte testType) { builder.addByte(7, testType, 0); }
@@ -57,11 +58,14 @@ public class Monster extends Table {
   public static void addTest4(FlatBufferBuilder builder, int test4Offset) { builder.addOffset(9, test4Offset, 0); }
   public static void startTest4Vector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems, 2); }
   public static void addTestarrayofstring(FlatBufferBuilder builder, int testarrayofstringOffset) { builder.addOffset(10, testarrayofstringOffset, 0); }
+  public static int createTestarrayofstringVector(FlatBufferBuilder builder, int[] data) { builder.startVector(4, data.length, 4); for (int i = data.length - 1; i >= 0; i--) builder.addOffset(data[i]); return builder.endVector(); }
   public static void startTestarrayofstringVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems, 4); }
   public static void addTestarrayoftables(FlatBufferBuilder builder, int testarrayoftablesOffset) { builder.addOffset(11, testarrayoftablesOffset, 0); }
+  public static int createTestarrayoftablesVector(FlatBufferBuilder builder, int[] data) { builder.startVector(4, data.length, 4); for (int i = data.length - 1; i >= 0; i--) builder.addOffset(data[i]); return builder.endVector(); }
   public static void startTestarrayoftablesVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems, 4); }
   public static void addEnemy(FlatBufferBuilder builder, int enemyOffset) { builder.addOffset(12, enemyOffset, 0); }
   public static void addTestnestedflatbuffer(FlatBufferBuilder builder, int testnestedflatbufferOffset) { builder.addOffset(13, testnestedflatbufferOffset, 0); }
+  public static int createTestnestedflatbufferVector(FlatBufferBuilder builder, byte[] data) { builder.startVector(1, data.length, 1); for (int i = data.length - 1; i >= 0; i--) builder.addByte(data[i]); return builder.endVector(); }
   public static void startTestnestedflatbufferVector(FlatBufferBuilder builder, int numElems) { builder.startVector(1, numElems, 1); }
   public static void addTestempty(FlatBufferBuilder builder, int testemptyOffset) { builder.addOffset(14, testemptyOffset, 0); }
   public static int endMonster(FlatBufferBuilder builder) { return builder.endObject(); }