1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.io.*;
18 import java.nio.ByteBuffer;
19 import java.nio.ByteOrder;
20 import java.nio.channels.FileChannel;
21 import MyGame.Example.*;
22 import NamespaceA.*;
23 import NamespaceA.NamespaceB.*;
24 import com.google.flatbuffers.ByteBufferUtil;
25 import static com.google.flatbuffers.Constants.*;
26 import com.google.flatbuffers.FlatBufferBuilder;
27 
28 class JavaTest {
main(String[] args)29     public static void main(String[] args) {
30 
31         // First, let's test reading a FlatBuffer generated by C++ code:
32         // This file was generated from monsterdata_test.json
33 
34         byte[] data = null;
35         File file = new File("monsterdata_test.mon");
36         RandomAccessFile f = null;
37         try {
38             f = new RandomAccessFile(file, "r");
39             data = new byte[(int)f.length()];
40             f.readFully(data);
41             f.close();
42         } catch(java.io.IOException e) {
43             System.out.println("FlatBuffers test: couldn't read file");
44             return;
45         }
46 
47         // Now test it:
48 
49         ByteBuffer bb = ByteBuffer.wrap(data);
50         TestBuffer(bb);
51 
52         // Second, let's create a FlatBuffer from scratch in Java, and test it also.
53         // We use an initial size of 1 to exercise the reallocation algorithm,
54         // normally a size larger than the typical FlatBuffer you generate would be
55         // better for performance.
56         FlatBufferBuilder fbb = new FlatBufferBuilder(1);
57 
58         TestBuilderBasics(fbb, true);
59         TestBuilderBasics(fbb, false);
60 
61         TestExtendedBuffer(fbb.dataBuffer().asReadOnlyBuffer());
62 
63         TestNamespaceNesting();
64 
65         TestNestedFlatBuffer();
66 
67         TestCreateByteVector();
68 
69         TestCreateUninitializedVector();
70 
71         TestByteBufferFactory();
72 
73         TestSizedInputStream();
74 
75         System.out.println("FlatBuffers test: completed successfully");
76     }
77 
TestEnums()78     static void TestEnums() {
79       TestEq(Color.name(Color.Red), "Red");
80       TestEq(Color.name(Color.Blue), "Blue");
81       TestEq(Any.name(Any.NONE), "NONE");
82       TestEq(Any.name(Any.Monster), "Monster");
83     }
84 
TestBuffer(ByteBuffer bb)85     static void TestBuffer(ByteBuffer bb) {
86         TestEq(Monster.MonsterBufferHasIdentifier(bb), true);
87 
88         Monster monster = Monster.getRootAsMonster(bb);
89 
90         TestEq(monster.hp(), (short)80);
91         TestEq(monster.mana(), (short)150);  // default
92 
93         TestEq(monster.name(), "MyMonster");
94         // monster.friendly() // can't access, deprecated
95 
96         Vec3 pos = monster.pos();
97         TestEq(pos.x(), 1.0f);
98         TestEq(pos.y(), 2.0f);
99         TestEq(pos.z(), 3.0f);
100         TestEq(pos.test1(), 3.0);
101         TestEq(pos.test2(), Color.Green);
102         Test t = pos.test3();
103         TestEq(t.a(), (short)5);
104         TestEq(t.b(), (byte)6);
105 
106         TestEq(monster.testType(), (byte)Any.Monster);
107         Monster monster2 = new Monster();
108         TestEq(monster.test(monster2) != null, true);
109         TestEq(monster2.name(), "Fred");
110 
111         TestEq(monster.inventoryLength(), 5);
112         int invsum = 0;
113         for (int i = 0; i < monster.inventoryLength(); i++)
114             invsum += monster.inventory(i);
115         TestEq(invsum, 10);
116 
117         // Alternative way of accessing a vector:
118         ByteBuffer ibb = monster.inventoryAsByteBuffer();
119         invsum = 0;
120         while (ibb.position() < ibb.limit())
121             invsum += ibb.get();
122         TestEq(invsum, 10);
123 
124         Test test_0 = monster.test4(0);
125         Test test_1 = monster.test4(1);
126         TestEq(monster.test4Length(), 2);
127         TestEq(test_0.a() + test_0.b() + test_1.a() + test_1.b(), 100);
128 
129         TestEq(monster.testarrayofstringLength(), 2);
130         TestEq(monster.testarrayofstring(0),"test1");
131         TestEq(monster.testarrayofstring(1),"test2");
132 
133         TestEq(monster.testbool(), true);
134     }
135 
136     // this method checks additional fields not present in the binary buffer read from file
137     // these new tests are performed on top of the regular tests
TestExtendedBuffer(ByteBuffer bb)138     static void TestExtendedBuffer(ByteBuffer bb) {
139         TestBuffer(bb);
140 
141         Monster monster = Monster.getRootAsMonster(bb);
142 
143         TestEq(monster.testhashu32Fnv1(), Integer.MAX_VALUE + 1L);
144     }
145 
TestNamespaceNesting()146     static void TestNamespaceNesting() {
147         // reference / manipulate these to verify compilation
148         FlatBufferBuilder fbb = new FlatBufferBuilder(1);
149 
150         TableInNestedNS.startTableInNestedNS(fbb);
151         TableInNestedNS.addFoo(fbb, 1234);
152         int nestedTableOff = TableInNestedNS.endTableInNestedNS(fbb);
153 
154         TableInFirstNS.startTableInFirstNS(fbb);
155         TableInFirstNS.addFooTable(fbb, nestedTableOff);
156         int off = TableInFirstNS.endTableInFirstNS(fbb);
157     }
158 
TestNestedFlatBuffer()159     static void TestNestedFlatBuffer() {
160         final String nestedMonsterName = "NestedMonsterName";
161         final short nestedMonsterHp = 600;
162         final short nestedMonsterMana = 1024;
163 
164         FlatBufferBuilder fbb1 = new FlatBufferBuilder(16);
165         int str1 = fbb1.createString(nestedMonsterName);
166         Monster.startMonster(fbb1);
167         Monster.addName(fbb1, str1);
168         Monster.addHp(fbb1, nestedMonsterHp);
169         Monster.addMana(fbb1, nestedMonsterMana);
170         int monster1 = Monster.endMonster(fbb1);
171         Monster.finishMonsterBuffer(fbb1, monster1);
172         byte[] fbb1Bytes = fbb1.sizedByteArray();
173         fbb1 = null;
174 
175         FlatBufferBuilder fbb2 = new FlatBufferBuilder(16);
176         int str2 = fbb2.createString("My Monster");
177         int nestedBuffer = Monster.createTestnestedflatbufferVector(fbb2, fbb1Bytes);
178         Monster.startMonster(fbb2);
179         Monster.addName(fbb2, str2);
180         Monster.addHp(fbb2, (short)50);
181         Monster.addMana(fbb2, (short)32);
182         Monster.addTestnestedflatbuffer(fbb2, nestedBuffer);
183         int monster = Monster.endMonster(fbb2);
184         Monster.finishMonsterBuffer(fbb2, monster);
185 
186         // Now test the data extracted from the nested buffer
187         Monster mons = Monster.getRootAsMonster(fbb2.dataBuffer());
188         Monster nestedMonster = mons.testnestedflatbufferAsMonster();
189 
190         TestEq(nestedMonsterMana, nestedMonster.mana());
191         TestEq(nestedMonsterHp, nestedMonster.hp());
192         TestEq(nestedMonsterName, nestedMonster.name());
193     }
194 
TestCreateByteVector()195     static void TestCreateByteVector() {
196         FlatBufferBuilder fbb = new FlatBufferBuilder(16);
197         int str = fbb.createString("MyMonster");
198         byte[] inventory = new byte[] { 0, 1, 2, 3, 4 };
199         int vec = fbb.createByteVector(inventory);
200         Monster.startMonster(fbb);
201         Monster.addInventory(fbb, vec);
202         Monster.addName(fbb, str);
203         int monster1 = Monster.endMonster(fbb);
204         Monster.finishMonsterBuffer(fbb, monster1);
205         Monster monsterObject = Monster.getRootAsMonster(fbb.dataBuffer());
206 
207         TestEq(monsterObject.inventory(1), (int)inventory[1]);
208         TestEq(monsterObject.inventoryLength(), inventory.length);
209         TestEq(ByteBuffer.wrap(inventory), monsterObject.inventoryAsByteBuffer());
210     }
211 
TestCreateUninitializedVector()212     static void TestCreateUninitializedVector() {
213         FlatBufferBuilder fbb = new FlatBufferBuilder(16);
214         int str = fbb.createString("MyMonster");
215         byte[] inventory = new byte[] { 0, 1, 2, 3, 4 };
216         ByteBuffer bb = fbb.createUnintializedVector(1, inventory.length, 1);
217         for (byte i:inventory) {
218             bb.put(i);
219         }
220         int vec = fbb.endVector();
221         Monster.startMonster(fbb);
222         Monster.addInventory(fbb, vec);
223         Monster.addName(fbb, str);
224         int monster1 = Monster.endMonster(fbb);
225         Monster.finishMonsterBuffer(fbb, monster1);
226         Monster monsterObject = Monster.getRootAsMonster(fbb.dataBuffer());
227 
228         TestEq(monsterObject.inventory(1), (int)inventory[1]);
229         TestEq(monsterObject.inventoryLength(), inventory.length);
230         TestEq(ByteBuffer.wrap(inventory), monsterObject.inventoryAsByteBuffer());
231     }
232 
TestByteBufferFactory()233     static void TestByteBufferFactory() {
234         final class MappedByteBufferFactory implements FlatBufferBuilder.ByteBufferFactory {
235             @Override
236             public ByteBuffer newByteBuffer(int capacity) {
237                 ByteBuffer bb;
238                 try {
239                     bb =  new RandomAccessFile("javatest.bin", "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, capacity).order(ByteOrder.LITTLE_ENDIAN);
240                 } catch(Throwable e) {
241                     System.out.println("FlatBuffers test: couldn't map ByteBuffer to a file");
242                     bb = null;
243                 }
244                 return bb;
245             }
246         }
247 
248         FlatBufferBuilder fbb = new FlatBufferBuilder(1, new MappedByteBufferFactory());
249 
250         TestBuilderBasics(fbb, false);
251     }
252 
TestSizedInputStream()253     static void TestSizedInputStream() {
254         // Test on default FlatBufferBuilder that uses HeapByteBuffer
255         FlatBufferBuilder fbb = new FlatBufferBuilder(1);
256 
257         TestBuilderBasics(fbb, false);
258 
259         InputStream in = fbb.sizedInputStream();
260         byte[] array = fbb.sizedByteArray();
261         int count = 0;
262         int currentVal = 0;
263 
264         while (currentVal != -1 && count < array.length) {
265             try {
266                 currentVal = in.read();
267             } catch(java.io.IOException e) {
268                 System.out.println("FlatBuffers test: couldn't read from InputStream");
269                 return;
270             }
271             TestEq((byte)currentVal, array[count]);
272             count++;
273         }
274         TestEq(count, array.length);
275     }
276 
TestBuilderBasics(FlatBufferBuilder fbb, boolean sizePrefix)277     static void TestBuilderBasics(FlatBufferBuilder fbb, boolean sizePrefix) {
278         int[] names = {fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma")};
279         int[] off = new int[3];
280         Monster.startMonster(fbb);
281         Monster.addName(fbb, names[0]);
282         off[0] = Monster.endMonster(fbb);
283         Monster.startMonster(fbb);
284         Monster.addName(fbb, names[1]);
285         off[1] = Monster.endMonster(fbb);
286         Monster.startMonster(fbb);
287         Monster.addName(fbb, names[2]);
288         off[2] = Monster.endMonster(fbb);
289         int sortMons = fbb.createSortedVectorOfTables(new Monster(), off);
290 
291         // We set up the same values as monsterdata.json:
292 
293         int str = fbb.createString("MyMonster");
294 
295         int inv = Monster.createInventoryVector(fbb, new byte[] { 0, 1, 2, 3, 4 });
296 
297         int fred = fbb.createString("Fred");
298         Monster.startMonster(fbb);
299         Monster.addName(fbb, fred);
300         int mon2 = Monster.endMonster(fbb);
301 
302         Monster.startTest4Vector(fbb, 2);
303         Test.createTest(fbb, (short)10, (byte)20);
304         Test.createTest(fbb, (short)30, (byte)40);
305         int test4 = fbb.endVector();
306 
307         int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
308                 fbb.createString("test1"),
309                 fbb.createString("test2")
310         });
311 
312         Monster.startMonster(fbb);
313         Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
314                 Color.Green, (short)5, (byte)6));
315         Monster.addHp(fbb, (short)80);
316         Monster.addName(fbb, str);
317         Monster.addInventory(fbb, inv);
318         Monster.addTestType(fbb, (byte)Any.Monster);
319         Monster.addTest(fbb, mon2);
320         Monster.addTest4(fbb, test4);
321         Monster.addTestarrayofstring(fbb, testArrayOfString);
322         Monster.addTestbool(fbb, true);
323         Monster.addTesthashu32Fnv1(fbb, Integer.MAX_VALUE + 1L);
324         Monster.addTestarrayoftables(fbb, sortMons);
325         int mon = Monster.endMonster(fbb);
326 
327         if (sizePrefix) {
328             Monster.finishSizePrefixedMonsterBuffer(fbb, mon);
329         } else {
330             Monster.finishMonsterBuffer(fbb, mon);
331         }
332 
333         // Write the result to a file for debugging purposes:
334         // Note that the binaries are not necessarily identical, since the JSON
335         // parser may serialize in a slightly different order than the above
336         // Java code. They are functionally equivalent though.
337 
338         try {
339             String filename = "monsterdata_java_wire" + (sizePrefix ? "_sp" : "") + ".mon";
340             FileChannel fc = new FileOutputStream(filename).getChannel();
341             fc.write(fbb.dataBuffer().duplicate());
342             fc.close();
343         } catch(java.io.IOException e) {
344             System.out.println("FlatBuffers test: couldn't write file");
345             return;
346         }
347 
348         // Test it:
349         ByteBuffer dataBuffer = fbb.dataBuffer();
350         if (sizePrefix) {
351             TestEq(ByteBufferUtil.getSizePrefix(dataBuffer) + SIZE_PREFIX_LENGTH,
352                    dataBuffer.remaining());
353             dataBuffer = ByteBufferUtil.removeSizePrefix(dataBuffer);
354         }
355         TestExtendedBuffer(dataBuffer);
356 
357         // Make sure it also works with read only ByteBuffers. This is slower,
358         // since creating strings incurs an additional copy
359         // (see Table.__string).
360         TestExtendedBuffer(dataBuffer.asReadOnlyBuffer());
361 
362         TestEnums();
363 
364         //Attempt to mutate Monster fields and check whether the buffer has been mutated properly
365         // revert to original values after testing
366         Monster monster = Monster.getRootAsMonster(dataBuffer);
367 
368         // mana is optional and does not exist in the buffer so the mutation should fail
369         // the mana field should retain its default value
370         TestEq(monster.mutateMana((short)10), false);
371         TestEq(monster.mana(), (short)150);
372 
373         // Accessing a vector of sorted by the key tables
374         TestEq(monster.testarrayoftables(0).name(), "Barney");
375         TestEq(monster.testarrayoftables(1).name(), "Frodo");
376         TestEq(monster.testarrayoftables(2).name(), "Wilma");
377 
378         // Example of searching for a table by the key
379         TestEq(monster.testarrayoftablesByKey("Frodo").name(), "Frodo");
380         TestEq(monster.testarrayoftablesByKey("Barney").name(), "Barney");
381         TestEq(monster.testarrayoftablesByKey("Wilma").name(), "Wilma");
382 
383         // testType is an existing field and mutating it should succeed
384         TestEq(monster.testType(), (byte)Any.Monster);
385         TestEq(monster.mutateTestType(Any.NONE), true);
386         TestEq(monster.testType(), (byte)Any.NONE);
387         TestEq(monster.mutateTestType(Any.Monster), true);
388         TestEq(monster.testType(), (byte)Any.Monster);
389 
390         //mutate the inventory vector
391         TestEq(monster.mutateInventory(0, 1), true);
392         TestEq(monster.mutateInventory(1, 2), true);
393         TestEq(monster.mutateInventory(2, 3), true);
394         TestEq(monster.mutateInventory(3, 4), true);
395         TestEq(monster.mutateInventory(4, 5), true);
396 
397         for (int i = 0; i < monster.inventoryLength(); i++) {
398             TestEq(monster.inventory(i), i + 1);
399         }
400 
401         //reverse mutation
402         TestEq(monster.mutateInventory(0, 0), true);
403         TestEq(monster.mutateInventory(1, 1), true);
404         TestEq(monster.mutateInventory(2, 2), true);
405         TestEq(monster.mutateInventory(3, 3), true);
406         TestEq(monster.mutateInventory(4, 4), true);
407 
408         // get a struct field and edit one of its fields
409         Vec3 pos = monster.pos();
410         TestEq(pos.x(), 1.0f);
411         pos.mutateX(55.0f);
412         TestEq(pos.x(), 55.0f);
413         pos.mutateX(1.0f);
414         TestEq(pos.x(), 1.0f);
415     }
416 
TestEq(T a, T b)417     static <T> void TestEq(T a, T b) {
418         if (!a.equals(b)) {
419             System.out.println("" + a.getClass().getName() + " " + b.getClass().getName());
420             System.out.println("FlatBuffers test FAILED: \'" + a + "\' != \'" + b + "\'");
421             assert false;
422             System.exit(1);
423         }
424     }
425 }
426