/** Returns the field named {@code field} on the message type of {@code self}. */ Field dereference(Field self, String field) { if (field.startsWith("[") && field.endsWith("]")) { field = field.substring(1, field.length() - 1); } Type type = protoTypeNames.get(self.type().toString()); if (type instanceof MessageType) { MessageType messageType = (MessageType) type; Field messageField = messageType.field(field); if (messageField != null) return messageField; Map<String, Field> typeExtensions = messageType.extensionFieldsMap(); Field extensionField = resolve(field, typeExtensions); if (extensionField != null) return extensionField; } return null; // Unable to traverse this field path. }
public Field getField(ProtoMember protoMember) { Type type = getType(protoMember.type()); if (!(type instanceof MessageType)) return null; Field field = ((MessageType) type).field(protoMember.member()); if (field == null) { field = ((MessageType) type).extensionField(protoMember.member()); } return field; }
@Test public void scalarFieldIsPacked() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "message Message {\n" + " repeated int32 a = 1;\n" + " repeated int32 b = 2 [packed=false];\n" + " repeated int32 c = 3 [packed=true];\n" + "}\n") .schema(); MessageType message = (MessageType) schema.getType("Message"); assertThat(message.field("a").isPacked()).isFalse(); assertThat(message.field("b").isPacked()).isFalse(); assertThat(message.field("c").isPacked()).isTrue(); }
@Test public void fieldIsDeprecated() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "message Message {\n" + " optional int32 a = 1;\n" + " optional int32 b = 2 [deprecated=false];\n" + " optional int32 c = 3 [deprecated=true];\n" + "}\n") .schema(); MessageType message = (MessageType) schema.getType("Message"); assertThat(message.field("a").isDeprecated()).isFalse(); assertThat(message.field("b").isDeprecated()).isFalse(); assertThat(message.field("c").isDeprecated()).isTrue(); }
@Test public void earlierSourcesTakePrecedenceOverLaterSources() throws IOException { Files.createDirectories(fileSystem.getPath("/source1")); Files.createDirectories(fileSystem.getPath("/source2")); writeFile("/source1/message.proto", "" + "message Message {\n" + " optional string a = 1;\n" + "}"); writeFile("/source2/message.proto", "" + "message Message {\n" + " optional string b = 2;\n" + "}"); Schema schema = new SchemaLoader() .addSource(fileSystem.getPath("/source1")) .addSource(fileSystem.getPath("/source2")) .load(); MessageType message = (MessageType) schema.getType("Message"); assertThat(message.field("a")).isNotNull(); }
@Test public void enumFieldIsPacked() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "message Message {\n" + " repeated HabitablePlanet home_planet = 1 [packed=true];\n" + " enum HabitablePlanet {\n" + " EARTH = 1;\n" + " }\n" + "}\n") .schema(); MessageType message = (MessageType) schema.getType("Message"); assertThat(message.field("home_planet").isPacked()).isTrue(); }
/** When we include excludes only, the mark phase is skipped. */ @Test public void excludeWithoutInclude() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "message MessageA {\n" + " optional string b = 1;\n" + " optional string c = 2;\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .exclude("MessageA#c") .build()); assertThat(((MessageType) pruned.getType("MessageA")).field("b")).isNotNull(); assertThat(((MessageType) pruned.getType("MessageA")).field("c")).isNull(); }
@Test public void retainedOptionRetainsOptionsType() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "import \"google/protobuf/descriptor.proto\";\n" + "extend google.protobuf.FieldOptions {\n" + " optional string a = 22001;\n" + "}\n" + "message Message {\n" + " optional string f = 1 [a = \"a\"];\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .include("Message#f") .build()); assertThat(((MessageType) pruned.getType("Message")).field("f")).isNotNull(); assertThat(((MessageType) pruned.getType("google.protobuf.FieldOptions"))).isNotNull(); }
@Test public void excludeField() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "message MessageA {\n" + " optional string b = 1;\n" + " optional string c = 2;\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .include("MessageA") .exclude("MessageA#c") .build()); assertThat(((MessageType) pruned.getType("MessageA")).field("b")).isNotNull(); assertThat(((MessageType) pruned.getType("MessageA")).field("c")).isNull(); }
@Test public void retainField() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "message MessageA {\n" + " optional string b = 1;\n" + " map<string, string> c = 2;\n" + "}\n" + "message MessageB {\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .include("MessageA#b") .build()); assertThat(((MessageType) pruned.getType("MessageA")).field("b")).isNotNull(); assertThat(((MessageType) pruned.getType("MessageA")).field("c")).isNull(); assertThat(pruned.getType("MessageB")).isNull(); }
@Test public void messsageAndExtensionNameCollision() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "message Message {\n" + " optional string a = 1;\n" + "}\n") .add("extend.proto", "" + "package p;\n" + "import \"message.proto\";\n" + "extend Message {\n" + " optional string a = 2;\n" + "}\n") .schema(); MessageType messageType = (MessageType) schema.getType("Message"); assertThat(messageType.field("a").tag()).isEqualTo(1); assertThat(messageType.extensionField("p.a").tag()).isEqualTo(2); }
@Test public void retainExtension() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "message Message {\n" + " optional string a = 1;\n" + "}\n" + "extend Message {\n" + " optional string b = 2;\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .include("Message") .build()); assertThat(((MessageType) pruned.getType("Message")).field("a")).isNotNull(); assertThat(((MessageType) pruned.getType("Message")).extensionField("b")).isNotNull(); }
@Test public void fieldOptions() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "import \"google/protobuf/descriptor.proto\";\n" + "message Message {\n" + " optional int32 a = 1;\n" + " optional int32 b = 2 [color=red, deprecated=true, packed=true];\n" + "}\n" + "extend google.protobuf.FieldOptions {\n" + " optional string color = 60001;\n" + "}\n") .schema(); MessageType message = (MessageType) schema.getType("Message"); Options aOptions = message.field("a").options(); assertThat(aOptions.get(ProtoMember.get(FIELD_OPTIONS, "color"))).isNull(); assertThat(aOptions.get(ProtoMember.get(FIELD_OPTIONS, "deprecated"))).isNull(); assertThat(aOptions.get(ProtoMember.get(FIELD_OPTIONS, "packed"))).isNull(); Options bOptions = message.field("b").options(); assertThat(bOptions.get(ProtoMember.get(FIELD_OPTIONS, "color"))).isEqualTo("red"); assertThat(bOptions.get(ProtoMember.get(FIELD_OPTIONS, "deprecated"))).isEqualTo("true"); assertThat(bOptions.get(ProtoMember.get(FIELD_OPTIONS, "packed"))).isEqualTo("true"); }
@Test public void importResolvesEnclosingPackageSuffix() throws Exception { Schema schema = new RepoBuilder() .add("a_b.proto", "" + "package a.b;\n" + "\n" + "message MessageB {\n" + "}\n") .add("a_b_c.proto", "" + "package a.b.c;\n" + "\n" + "import \"a_b.proto\";\n" + "\n" + "message MessageC {\n" + " optional b.MessageB message_b = 1;\n" + "}\n") .schema(); MessageType messageC = (MessageType) schema.getType("a.b.c.MessageC"); assertThat(messageC.field("message_b").type()).isEqualTo(ProtoType.get("a.b.MessageB")); }
@Test public void retainExtensionMembers() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "message Message {\n" + " optional string a = 1;\n" + " optional string b = 2;\n" + "}\n" + "extend Message {\n" + " optional string c = 3;\n" + " optional string d = 4;\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .include("Message#a") .include("Message#c") .build()); assertThat(((MessageType) pruned.getType("Message")).field("a")).isNotNull(); assertThat(((MessageType) pruned.getType("Message")).field("b")).isNull(); assertThat(((MessageType) pruned.getType("Message")).extensionField("c")).isNotNull(); assertThat(((MessageType) pruned.getType("Message")).extensionField("d")).isNull(); }
@Test public void importResolvesNestedPackageSuffix() throws Exception { Schema schema = new RepoBuilder() .add("a_b.proto", "" + "package a.b;\n" + "\n" + "import \"a_b_c.proto\";\n" + "\n" + "message MessageB {\n" + " optional c.MessageC message_c = 1;\n" + "}\n") .add("a_b_c.proto", "" + "package a.b.c;\n" + "\n" + "message MessageC {\n" + "}\n") .schema(); MessageType messageC = (MessageType) schema.getType("a.b.MessageB"); assertThat(messageC.field("message_c").type()).isEqualTo(ProtoType.get("a.b.c.MessageC")); }
@Test public void fieldTypeImported() throws Exception { Schema schema = new RepoBuilder() .add("a.proto", "" + "package pa;\n" + "import \"b.proto\";\n" + "message A {\n" + " optional pb.B b = 1;\n" + "}\n") .add("b.proto", "" + "package pb;\n" + "message B {\n" + "}\n") .schema(); MessageType a = (MessageType) schema.getType("pa.A"); MessageType b = (MessageType) schema.getType("pb.B"); assertThat(a.field("b").type()).isEqualTo(b.type()); }
@Test public void fieldMapTypeImported() throws Exception { Schema schema = new RepoBuilder() .add("a.proto", "" + "package pa;\n" + "import \"b.proto\";\n" + "message A {\n" + " map<string, pb.B> b = 1;\n" + "}\n") .add("b.proto", "" + "package pb;\n" + "message B {\n" + "}\n") .schema(); MessageType a = (MessageType) schema.getType("pa.A"); MessageType b = (MessageType) schema.getType("pb.B"); assertThat(a.field("b").type().valueType()).isEqualTo(b.type()); }
@Test public void excludedFieldPrunesTopLevelOption() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "import \"google/protobuf/descriptor.proto\";\n" + "extend google.protobuf.FieldOptions {\n" + " optional string a = 22001;\n" + " optional string b = 22002;\n" + "}\n" + "message Message {\n" + " optional string f = 1 [a = \"a\", b = \"b\"];\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .exclude("google.protobuf.FieldOptions#b") .build()); Field field = ((MessageType) pruned.getType("Message")).field("f"); assertThat(field.options().get(ProtoMember.get(FIELD_OPTIONS, "a"))).isEqualTo("a"); assertThat(field.options().get(ProtoMember.get(FIELD_OPTIONS, "b"))).isNull(); }
@Test public void excludeOptions() throws Exception { Schema schema = new RepoBuilder() .add("service.proto", "" + "import \"google/protobuf/descriptor.proto\";\n" + "extend google.protobuf.FieldOptions {\n" + " optional string a = 22001;\n" + " optional string b = 22002;\n" + "}\n" + "message Message {\n" + " optional string f = 1 [ a = \"a\", b = \"b\" ];\n" + "}\n") .schema(); Schema pruned = schema.prune(new IdentifierSet.Builder() .exclude("google.protobuf.FieldOptions") .build()); Field field = ((MessageType) pruned.getType("Message")).field("f"); assertThat(field.options().map()).isEmpty(); }