private CodeBlock defaultValue(Field field) { Object defaultValue = field.getDefault(); if (defaultValue == null && isEnum(field.type())) { defaultValue = enumDefault(field.type()).name(); } if (field.type().isScalar() || defaultValue != null) { return fieldInitializer(field.type(), defaultValue); } throw new IllegalStateException("Field " + field + " cannot have default value"); }
/** 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. }
/** Returns the initial value of {@code field}, or null if it is doesn't have one. */ private @Nullable CodeBlock initialValue(Field field) { if (field.isPacked() || field.isRepeated()) { return CodeBlock.of("$T.newMutableList()", Internal.class); } else if (field.type().isMap()) { return CodeBlock.of("$T.newMutableMap()", Internal.class); } else { return null; } }
private MethodSpec setter( NameAllocator nameAllocator, TypeName builderType, OneOf oneOf, Field field) { TypeName javaType = fieldType(field); String fieldName = nameAllocator.get(field); MethodSpec.Builder result = MethodSpec.methodBuilder(fieldName) .addModifiers(PUBLIC) .addParameter(javaType, fieldName) .returns(builderType); if (!field.documentation().isEmpty()) { result.addJavadoc("$L\n", sanitizeJavadoc(field.documentation())); } if (field.isDeprecated()) { result.addAnnotation(Deprecated.class); } if (field.isRepeated() || field.type().isMap()) { result.addStatement("$T.checkElementsNotNull($L)", Internal.class, fieldName); } result.addStatement("this.$L = $L", fieldName, fieldName); if (oneOf != null) { for (Field other : oneOf.fields()) { if (field != other) { result.addStatement("this.$L = null", nameAllocator.get(other)); } } } result.addStatement("return this"); return result.build(); }
private TypeName fieldType(Field field) { ProtoType type = field.type(); if (type.isMap()) { return ParameterizedTypeName.get(ClassName.get(Map.class), typeName(type.keyType()), typeName(type.valueType())); } TypeName messageType = typeName(type); return field.isRepeated() ? listOf(messageType) : messageType; }
private MethodSpec messageToString(NameAllocator nameAllocator, MessageType type) { NameAllocator localNameAllocator = nameAllocator.clone(); MethodSpec.Builder result = MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(PUBLIC) .returns(String.class); String builderName = localNameAllocator.newName("builder"); result.addStatement("$1T $2N = new $1T()", StringBuilder.class, builderName); for (Field field : type.fieldsAndOneOfFields()) { String fieldName = nameAllocator.get(field); if (field.isRepeated() || field.type().isMap()) { result.addCode("if (!$N.isEmpty()) ", fieldName); } else if (!field.isRequired()) { result.addCode("if ($N != null) ", fieldName); } if (field.isRedacted()) { result.addStatement("$N.append(\", $N=██\")", builderName, field.name()); } else { result.addStatement("$N.append(\", $N=\").append($L)", builderName, field.name(), fieldName); } } result.addStatement("return builder.replace(0, 2, \"$L{\").append('}').toString()", type.type().simpleName()); return result.build(); }
private void markField(ProtoType declaringType, Field field) { if (marks.contains(ProtoMember.get(declaringType, field.name()))) { markOptions(field.options()); mark(field.type()); } }
private AnnotationSpec wireFieldAnnotation(Field field) { AnnotationSpec.Builder result = AnnotationSpec.builder(WireField.class); int tag = field.tag(); result.addMember("tag", String.valueOf(tag)); if (field.type().isMap()) { result.addMember("keyAdapter", "$S", adapterString(field.type().keyType())); result.addMember("adapter", "$S", adapterString(field.type().valueType())); } else { result.addMember("adapter", "$S", adapterString(field.type())); } if (!field.isOptional()) { if (field.isPacked()) { result.addMember("label", "$T.PACKED", WireField.Label.class); } else if (field.label() != null) { result.addMember("label", "$T.$L", WireField.Label.class, field.label()); } } if (field.isRedacted()) { result.addMember("redacted", "true"); } return result.build(); }
private FieldSpec optionsField(ProtoType optionsType, String fieldName, Options options) { TypeName optionsJavaType = typeName(optionsType); CodeBlock.Builder initializer = CodeBlock.builder(); initializer.add("$[new $T.Builder()", optionsJavaType); boolean empty = true; for (Map.Entry<ProtoMember, ?> entry : options.map().entrySet()) { if (entry.getKey().equals(FIELD_DEPRECATED) || entry.getKey().equals(PACKED)) { continue; } Field optionField = schema.getField(entry.getKey()); initializer.add("\n.$L($L)", fieldName(optionsType, optionField), fieldInitializer(optionField.type(), entry.getValue())); empty = false; } initializer.add("\n.build()$]"); if (empty) return null; return FieldSpec.builder(optionsJavaType, fieldName) .addModifiers(PUBLIC, STATIC, FINAL) .initializer(initializer.build()) .build(); }
@Test public void importSamePackageDifferentFile() throws Exception { Schema schema = new RepoBuilder() .add("a_b_1.proto", "" + "package a.b;\n" + "\n" + "import \"a_b_2.proto\";\n" + "\n" + "message MessageB {\n" + " optional .a.b.MessageC c1 = 1;\n" + " optional a.b.MessageC c2 = 2;\n" + " optional b.MessageC c3 = 3;\n" + " optional MessageC c4 = 4;\n" + "}\n") .add("a_b_2.proto", "" + "package a.b;\n" + "\n" + "message MessageC {\n" + "}\n") .schema(); MessageType messageC = (MessageType) schema.getType("a.b.MessageB"); assertThat(messageC.field("c1").type()).isEqualTo(ProtoType.get("a.b.MessageC")); assertThat(messageC.field("c2").type()).isEqualTo(ProtoType.get("a.b.MessageC")); assertThat(messageC.field("c3").type()).isEqualTo(ProtoType.get("a.b.MessageC")); assertThat(messageC.field("c4").type()).isEqualTo(ProtoType.get("a.b.MessageC")); }
@Test public void linkMessage() throws Exception { Schema schema = new RepoBuilder() .add("message.proto", "" + "import \"foo.proto\";\n" + "message Message {\n" + " optional foo_package.Foo field = 1;\n" + " map<string, foo_package.Bar> bars = 2;\n" + "}\n") .add("foo.proto", "" + "package foo_package;\n" + "message Foo {\n" + "}\n" + "message Bar {\n" + "}\n") .schema(); MessageType message = (MessageType) schema.getType("Message"); Field field = message.field("field"); assertThat(field.type()).isEqualTo(schema.getType("foo_package.Foo").type()); ProtoType bars = message.field("bars").type(); assertThat(bars.keyType()).isEqualTo(ProtoType.STRING); assertThat(bars.valueType()).isEqualTo(schema.getType("foo_package.Bar").type()); }
@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 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")); }
private CodeBlock decodeAndAssign(Field field, NameAllocator nameAllocator, boolean useBuilder) { String fieldName = nameAllocator.get(field); CodeBlock decode = CodeBlock.of("$L.decode(reader)", singleAdapterFor(field)); if (field.isRepeated()) { return useBuilder ? CodeBlock.of("builder.$L.add($L)", fieldName, decode) : CodeBlock.of("$L.add($L)", fieldName, decode); } else if (field.type().isMap()) { return useBuilder ? CodeBlock.of("builder.$L.putAll($L)", fieldName, decode) : CodeBlock.of("$L.putAll($L)", fieldName, decode); } else { return useBuilder ? CodeBlock.of("builder.$L($L)", fieldName, decode) : CodeBlock.of("$L = $L", fieldName, decode); } }
@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()); }
@Ignore("Resolution happens from the root not from inside Outer and so this fails.") @Test public void linkExtendTypeInOuterMessage() throws Exception { Schema schema = new RepoBuilder() .add("foo.proto", "" + "message Other {\n" + " extensions 1;\n" + "}\n" + "message Outer {\n" + " enum Choice {\n" + " ZERO = 0;\n" + " ONE = 1;\n" + " }\n" + "\n" + " extend Other {\n" + " optional Choice choice = 1;\n" + " }\n" + "}") .schema(); MessageType message = (MessageType) schema.getType("Other"); Field field = message.field("choice"); assertThat(field.type()).isEqualTo(schema.getType("Outer.Choice").type()); }
@Test public void dotPrefixRefersToRootPackage() throws Exception { Schema schema = new RepoBuilder() .add("a.proto", "" + "package a;\n" + "\n" + "message MessageA {\n" + "}\n") .add("a_b.proto", "" + "package a.b;\n" + "\n" + "import \"a.proto\";\n" + "import \"a_b_a.proto\";\n" + "\n" + "message MessageB {\n" + " optional .a.MessageA message_a = 1;\n" + "}\n") .add("a_b_a.proto", "" + "package a.b.a;\n" + "\n" + "message MessageA {\n" + "}\n") .schema(); MessageType messageC = (MessageType) schema.getType("a.b.MessageB"); assertThat(messageC.field("message_a").type()).isEqualTo(ProtoType.get("a.MessageA")); }
@Test public void transitivePublicImportFollowed() throws Exception { Schema schema = new RepoBuilder() .add("a.proto", "" + "package pa;\n" + "import \"b.proto\";\n" + "message A {\n" + " optional pc.C c = 1;\n" + "}\n") .add("b.proto", "" + "package pb;\n" + "import public \"c.proto\";\n" + "message B {\n" + "}\n") .add("c.proto", "" + "package pc;\n" + "message C {\n" + "}\n") .schema(); MessageType a = (MessageType) schema.getType("pa.A"); MessageType c = (MessageType) schema.getType("pc.C"); assertThat(a.field("c").type()).isEqualTo(c.type()); }