@Test public void structuredAndUnstructuredOptions() throws Exception { // From https://developers.google.com/protocol-buffers/docs/proto#options Schema schema = new SchemaBuilder() .add( "foo.proto", "" + "import \"google/protobuf/descriptor.proto\";\n" + "message FooOptions {\n" + " optional int32 opt1 = 1;\n" + " optional string opt2 = 2;\n" + "}\n" + "\n" + "extend google.protobuf.FieldOptions {\n" + " optional FooOptions foo_options = 1234;\n" + "}\n" + "\n" + "message Bar {\n" + " optional int32 a = 1 [(foo_options).opt1 = 123, (foo_options).opt2 = \"baz\"];\n" + " optional int32 b = 2 [(foo_options) = { opt1: 456 opt2: \"quux\" }];\n" + "}\n") .add("google/protobuf/descriptor.proto") .build(); ProtoMember fooOptions = ProtoMember.get(Options.FIELD_OPTIONS, "foo_options"); ProtoMember opt1 = ProtoMember.get(ProtoType.get("FooOptions"), "opt1"); ProtoMember opt2 = ProtoMember.get(ProtoType.get("FooOptions"), "opt2"); MessageType bar = (MessageType) schema.getType("Bar"); assertThat(bar.field("a").options().map()) .isEqualTo(ImmutableMap.of(fooOptions, ImmutableMap.of(opt1, "123", opt2, "baz"))); assertThat(bar.field("b").options().map()) .isEqualTo(ImmutableMap.of(fooOptions, ImmutableMap.of(opt1, "456", opt2, "quux"))); }
Map<ProtoMember, Object> canonicalizeOption( Linker linker, ProtoType extensionType, OptionElement option) { Type type = linker.get(extensionType); if (!(type instanceof MessageType)) { return null; // No known extensions for the given extension type. } MessageType messageType = (MessageType) type; String[] path; Field field = messageType.field(option.name()); if (field != null) { // This is an option declared by descriptor.proto. path = new String[] {option.name()}; } else { // This is an option declared by an extension. Map<String, Field> extensionsForType = messageType.extensionFieldsMap(); path = resolveFieldPath(option.name(), extensionsForType.keySet()); String packageName = linker.packageName(); if (path == null && packageName != null) { // If the path couldn't be resolved, attempt again by prefixing it with the package name. path = resolveFieldPath(packageName + "." + option.name(), extensionsForType.keySet()); } if (path == null) { return null; // Unable to find the root of this field path. } field = extensionsForType.get(path[0]); } Map<ProtoMember, Object> result = new LinkedHashMap<>(); Map<ProtoMember, Object> last = result; ProtoType lastProtoType = messageType.type(); for (int i = 1; i < path.length; i++) { Map<ProtoMember, Object> nested = new LinkedHashMap<>(); last.put(ProtoMember.get(lastProtoType, field), nested); lastProtoType = field.type(); last = nested; field = linker.dereference(field, path[i]); if (field == null) { return null; // Unable to dereference this path segment. } } last.put( ProtoMember.get(lastProtoType, field), canonicalizeValue(linker, field, option.value())); return result; }