diff --git a/compiler/src/main/cpp/capnpc-java.c++ b/compiler/src/main/cpp/capnpc-java.c++ index 96bc5f1..57d0c31 100644 --- a/compiler/src/main/cpp/capnpc-java.c++ +++ b/compiler/src/main/cpp/capnpc-java.c++ @@ -1186,8 +1186,17 @@ private: kj::String defaultParams = defaultOffset == 0 ? kj::str("null, 0") : kj::str( "Schemas.b_", kj::hex(typeId), ", ", defaultOffset); + auto typeParamVec = getTypeParameters(field.getContainingStruct()); kj::String listFactory = makeFactoryArg(field.getType()); + bool isGeneric = false; + { + auto type = field.getType().asList().getElementType(); + if (type.isStruct()) { + isGeneric = type.asStruct().getProto().getIsGeneric(); + } + } + return FieldText { kj::strTree( kj::mv(unionDiscrim.readerIsDef), @@ -1195,10 +1204,28 @@ private: spaces(indent), " return !_pointerFieldIsNull(", offset, ");\n", spaces(indent), " }\n", - spaces(indent), " public final ", readerType, - " get", titleCase, "() {\n", - spaces(indent), " return _getPointerField(", listFactory, ", ", offset, ", ", defaultParams, ");\n", - spaces(indent), " }\n", + (isGeneric ? + kj::strTree( + spaces(indent), " public final ", + (typeParamVec.size() == 0 ? kj::strTree() : + kj::strTree( + "<", + kj::StringTree(KJ_MAP(p, typeParamVec) { + return kj::strTree(p, "_Builder"); + }, ", "), + "> ")), + readerType, + " get", titleCase, "( org.capnproto.ListFactory<", builderType, ", ", readerType, "> factory) {\n", + spaces(indent), " return _getPointerField(factory,", offset, ");\n", + spaces(indent), " }\n" + ) : + kj::strTree( + spaces(indent), " public final ", readerType, + " get", titleCase, "() {\n", + spaces(indent), " return _getPointerField(", listFactory, ", ", offset, ", ", defaultParams, ");\n", + spaces(indent), " }\n" + ) + ), "\n"), kj::strTree( @@ -1207,19 +1234,75 @@ private: spaces(indent), " return !_pointerFieldIsNull(", offset, ");\n", spaces(indent), " }\n", - spaces(indent), " public final ", builderType, - " get", titleCase, "() {\n", - spaces(indent), " return _getPointerField(", listFactory, ", ", offset, ", ", defaultParams, ");\n", - spaces(indent), " }\n", + (isGeneric ? + kj::strTree( + spaces(indent), " public final ", + (typeParamVec.size() == 0 ? kj::strTree() : + kj::strTree( + "<", + kj::StringTree(KJ_MAP(p, typeParamVec) { + return kj::strTree(p, "_Reader"); + }, ", "), + "> ")), + builderType, + " get", titleCase, "( org.capnproto.ListFactory<", builderType, ", ", readerType, "> factory) {\n", + spaces(indent), " return _getPointerField(factory,", offset, ");\n", + spaces(indent), " }\n" + ) : + kj::strTree( + spaces(indent), " public final ", builderType, + " get", titleCase, "() {\n", + spaces(indent), " return _getPointerField(", listFactory, ", ", offset, ", ", defaultParams, ");\n", + spaces(indent), " }\n" + ) + ), + + (isGeneric ? + kj::strTree( + spaces(indent), " public final ", + (typeParamVec.size() == 0 ? kj::strTree() : + kj::strTree( + "<", + kj::StringTree(KJ_MAP(p, typeParamVec) { + return kj::strTree(p, "_Reader"); + }, ", "), + "> ")), + "void set", titleCase, + "(org.capnproto.SetPointerBuilder<", builderType, ", ", readerType, "> factory, ", readerType, " value) {\n", + spaces(indent), " _setPointerField(factory, ", offset, ", value);\n", + spaces(indent), " }\n" + ) : + kj::strTree( + spaces(indent), " public final void set", titleCase, "(", readerType, " value) {\n", + spaces(indent), " _setPointerField(", listFactory, ", ", offset, ", value);\n", + spaces(indent), " }\n" + ) + ), + + (isGeneric ? + kj::strTree( + spaces(indent), " public final ", + (typeParamVec.size() == 0 ? kj::strTree() : + kj::strTree( + "<", + kj::StringTree(KJ_MAP(p, typeParamVec) { + return kj::strTree(p, "_Reader"); + }, ", "), + "> ")), + builderType, + " init", titleCase, "( org.capnproto.ListFactory<", builderType, ", ", readerType, "> factory, int size) {\n", + spaces(indent), " return _initPointerField(factory, ", offset, ", size);\n", + spaces(indent), " }\n" + ) : + kj::strTree( + spaces(indent), " public final ", builderType, + " init", titleCase, "(int size) {\n", + spaces(indent), " return _initPointerField(", listFactory, ", ", offset, ", size);\n", + spaces(indent), " }\n") + ) + ), - spaces(indent), " public final void set", titleCase, "(", readerType, " value) {\n", - spaces(indent), " _setPointerField(", listFactory, ", ", offset, ", value);\n", - spaces(indent), " }\n", - spaces(indent), " public final ", builderType, - " init", titleCase, "(int size) {\n", - spaces(indent), " return _initPointerField(", listFactory, ", ", offset, ", size);\n", - spaces(indent), " }\n"), }; } else { KJ_UNREACHABLE; diff --git a/compiler/src/test/scala/org/capnproto/EncodingSuite.scala b/compiler/src/test/scala/org/capnproto/EncodingSuite.scala index 8a62f47..3658449 100644 --- a/compiler/src/test/scala/org/capnproto/EncodingSuite.scala +++ b/compiler/src/test/scala/org/capnproto/EncodingSuite.scala @@ -609,6 +609,47 @@ class EncodingSuite extends FunSuite { TestUtil.checkTestMessage(field.asReader()) } + test("GenericMap") { + val builder = new MessageBuilder() + val mapFactory = new GenericMap.Factory(Text.factory, TestAllTypes.factory) + val entryFactory = new StructList.Factory(new GenericMap.Entry.Factory(Text.factory, TestAllTypes.factory)); + val root = builder.initRoot(mapFactory) + + { + val entries = root.initEntries(entryFactory, 3); + + val entry0 = entries.get(0) + entry0.setKey(Text.factory, new Text.Reader("foo")) + val value0 = entry0.initValue() + value0.setInt64Field(101); + + val entry1 = entries.get(1) + entry1.setKey(Text.factory, new Text.Reader("bar")) + val value1 = entry1.initValue() + value1.setInt64Field(202); + + val entry2 = entries.get(2) + entry2.setKey(Text.factory, new Text.Reader("baz")) + val value2 = entry2.initValue() + value2.setInt64Field(303); + } + + { + val entries = root.asReader(mapFactory).getEntries(entryFactory) + val entry0 = entries.get(0) + assert(entry0.getKey().toString() == "foo") + assert(entry0.getValue().getInt64Field() == 101) + + val entry1 = entries.get(1) + assert(entry1.getKey().toString() == "bar") + assert(entry1.getValue().getInt64Field() == 202) + + val entry2 = entries.get(2) + assert(entry2.getKey().toString() == "baz") + assert(entry2.getValue().getInt64Field == 303) + } + } + // to debug, do this: //Serialize.write((new java.io.FileOutputStream("/Users/dwrensha/Desktop/test.dat")).getChannel(), // message) diff --git a/compiler/src/test/schema/test.capnp b/compiler/src/test/schema/test.capnp index 6fabea9..3352781 100644 --- a/compiler/src/test/schema/test.capnp +++ b/compiler/src/test/schema/test.capnp @@ -377,6 +377,16 @@ struct TestUseGenerics { listOfGenerics @11 :List(TestGenerics(TestAllTypes, Text)); } +struct GenericMap(K, V) +{ + entries @0 :List(Entry); + + struct Entry + { + key @0 :K; + value @1 :V; + } +} struct TestEmptyStruct {}