Add exemplar variants of the Java Any.is() and Any.unpack() methods.

The Java Any.is() and Any.unpack() methods now accept an exemplar message in
place of a Java class. This avoids the need to use Java introspection in the
implementation of these methods.  The exemplar variant of Any.is() is named
Any.isSameTypeAs().  The exemplar variant of Any.unpack() is named Any.unpackSameTypeAs().

PiperOrigin-RevId: 486748727
pull/10933/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent ab3dbe5d4b
commit 60b71498d7
  1. 96
      java/core/src/test/java/com/google/protobuf/AnyTest.java
  2. 4
      objectivec/GPBAny.pbobjc.h
  3. 4
      src/google/protobuf/any.proto
  4. 36
      src/google/protobuf/compiler/java/message.cc

@ -43,7 +43,6 @@ import org.junit.runners.JUnit4;
/** Unit tests for Any message. */
@RunWith(JUnit4.class)
public class AnyTest {
@Test
public void testAnyGeneratedApi() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@ -78,6 +77,40 @@ public class AnyTest {
}
}
@Test
public void testAnyGeneratedExemplarApi() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TestUtil.setAllFields(builder);
TestAllTypes message = builder.build();
TestAny container = TestAny.newBuilder().setValue(Any.pack(message)).build();
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
TestAllTypes result = container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
TestUtil.assertAllFieldsSet(result);
// Unpacking to a wrong exemplar will throw an exception.
try {
container.getValue().unpackSameTypeAs(TestAny.getDefaultInstance());
assertWithMessage("Exception is expected.").fail();
} catch (InvalidProtocolBufferException e) {
// expected.
}
// Test that unpacking throws an exception if parsing fails.
TestAny.Builder containerBuilder = container.toBuilder();
containerBuilder.getValueBuilder().setValue(ByteString.copyFrom(new byte[] {0x11}));
container = containerBuilder.build();
try {
container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
assertWithMessage("Exception is expected.").fail();
} catch (InvalidProtocolBufferException e) {
// expected.
}
}
@Test
public void testCustomTypeUrls() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@ -90,7 +123,9 @@ public class AnyTest {
.isEqualTo("xxx.com/" + TestAllTypes.getDescriptor().getFullName());
assertThat(container.getValue().is(TestAllTypes.class)).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().is(TestAny.class)).isFalse();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
TestAllTypes result = container.getValue().unpack(TestAllTypes.class);
TestUtil.assertAllFieldsSet(result);
@ -101,7 +136,9 @@ public class AnyTest {
.isEqualTo("yyy.com/" + TestAllTypes.getDescriptor().getFullName());
assertThat(container.getValue().is(TestAllTypes.class)).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().is(TestAny.class)).isFalse();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
result = container.getValue().unpack(TestAllTypes.class);
TestUtil.assertAllFieldsSet(result);
@ -112,12 +149,54 @@ public class AnyTest {
.isEqualTo("/" + TestAllTypes.getDescriptor().getFullName());
assertThat(container.getValue().is(TestAllTypes.class)).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().is(TestAny.class)).isFalse();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
result = container.getValue().unpack(TestAllTypes.class);
TestUtil.assertAllFieldsSet(result);
}
@Test
public void testCustomTypeUrlsWithExemplars() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TestUtil.setAllFields(builder);
TestAllTypes message = builder.build();
TestAny container = TestAny.newBuilder().setValue(Any.pack(message, "xxx.com")).build();
assertThat(container.getValue().getTypeUrl())
.isEqualTo("xxx.com/" + TestAllTypes.getDescriptor().getFullName());
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
TestAllTypes result = container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
TestUtil.assertAllFieldsSet(result);
container = TestAny.newBuilder().setValue(Any.pack(message, "yyy.com/")).build();
assertThat(container.getValue().getTypeUrl())
.isEqualTo("yyy.com/" + TestAllTypes.getDescriptor().getFullName());
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
result = container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
TestUtil.assertAllFieldsSet(result);
container = TestAny.newBuilder().setValue(Any.pack(message, "")).build();
assertThat(container.getValue().getTypeUrl())
.isEqualTo("/" + TestAllTypes.getDescriptor().getFullName());
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
assertThat(container.getValue().isSameTypeAs(TestAny.getDefaultInstance())).isFalse();
result = container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
TestUtil.assertAllFieldsSet(result);
}
@Test
public void testCachedUnpackResult() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@ -132,4 +211,19 @@ public class AnyTest {
TestAllTypes result2 = container.getValue().unpack(TestAllTypes.class);
assertThat(Objects.equals(result1, result2)).isTrue();
}
@Test
public void testCachedUnpackExemplarResult() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TestUtil.setAllFields(builder);
TestAllTypes message = builder.build();
TestAny container = TestAny.newBuilder().setValue(Any.pack(message)).build();
assertThat(container.getValue().isSameTypeAs(TestAllTypes.getDefaultInstance())).isTrue();
TestAllTypes result1 = container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
TestAllTypes result2 = container.getValue().unpackSameTypeAs(TestAllTypes.getDefaultInstance());
assertThat(Objects.equals(result1, result2)).isTrue();
}
}

@ -69,6 +69,10 @@ typedef GPB_ENUM(GPBAny_FieldNumber) {
* if (any.is(Foo.class)) {
* foo = any.unpack(Foo.class);
* }
* // or ...
* if (any.isSameTypeAs(Foo.getDefaultInstance())) {
* foo = any.unpack(Foo.getDefaultInstance());
* }
*
* Example 3: Pack and unpack a message in Python.
*

@ -63,6 +63,10 @@ option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// if (any.is(Foo.class)) {
// foo = any.unpack(Foo.class);
// }
// // or ...
// if (any.isSameTypeAs(Foo.getDefaultInstance())) {
// foo = any.unpack(Foo.getDefaultInstance());
// }
//
// Example 3: Pack and unpack a message in Python.
//

@ -1372,12 +1372,14 @@ void ImmutableMessageGenerator::GenerateTopLevelKotlinMembers(
GenerateKotlinOrNull(printer);
}
void ImmutableMessageGenerator::GenerateKotlinOrNull(io::Printer* printer) const {
void ImmutableMessageGenerator::GenerateKotlinOrNull(
io::Printer* printer) const {
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (field->has_presence() && GetJavaType(field) == JAVATYPE_MESSAGE) {
printer->Print(
"public val $full_classname$OrBuilder.$camelcase_name$OrNull: $full_name$?\n"
"public val $full_classname$OrBuilder.$camelcase_name$OrNull: "
"$full_name$?\n"
" get() = if (has$name$()) get$name$() else null\n\n",
"full_classname",
EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)),
@ -1592,6 +1594,11 @@ void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) {
" defaultInstance.getDescriptorForType().getFullName());\n"
"}\n"
"\n"
"public boolean isSameTypeAs(com.google.protobuf.Message message) {\n"
" return getTypeNameFromTypeUrl(getTypeUrl()).equals(\n"
" message.getDescriptorForType().getFullName());\n"
"}\n"
"\n"
"@SuppressWarnings(\"serial\")\n"
"private volatile com.google.protobuf.Message cachedUnpackValue;\n"
"\n"
@ -1617,7 +1624,30 @@ void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) {
" .parseFrom(getValue());\n"
" cachedUnpackValue = result;\n"
" return result;\n"
"}\n");
"}\n"
"\n"
"@java.lang.SuppressWarnings(\"unchecked\")\n"
"public <T extends com.google.protobuf.Message> T unpackSameTypeAs("
"T message)\n"
" throws com.google.protobuf.InvalidProtocolBufferException {\n");
printer->Print(
" boolean invalidValue = false;\n"
" if (cachedUnpackValue != null) {\n"
" if (cachedUnpackValue.getClass() == message.getClass()) {\n"
" return (T) cachedUnpackValue;\n"
" }\n"
" invalidValue = true;\n"
" }\n"
" if (invalidValue || !isSameTypeAs(message)) {\n"
" throw new com.google.protobuf.InvalidProtocolBufferException(\n"
" \"Type of the Any message does not match the given "
"exemplar.\");\n"
" }\n"
" T result = (T) message.getParserForType().parseFrom(getValue());\n"
" cachedUnpackValue = result;\n"
" return result;\n"
"}\n"
"\n");
}
} // namespace java

Loading…
Cancel
Save