Fix Java maps reflection to call onChange to populate changes to parent
builders. Change-Id: Ibf6ae3c0fe6bc31f74b8018c81a5af461b1c24ea
This commit is contained in:
parent
21fb217e6a
commit
20042b72da
2 changed files with 132 additions and 89 deletions
|
@ -73,7 +73,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
|
||||
/** For use by generated code only. */
|
||||
protected UnknownFieldSet unknownFields;
|
||||
|
||||
|
||||
protected GeneratedMessage() {
|
||||
unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
@ -549,12 +549,12 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
* Gets the map field with the given field number. This method should be
|
||||
* overridden in the generated message class if the message contains map
|
||||
* fields.
|
||||
*
|
||||
*
|
||||
* Unlike other field types, reflection support for map fields can't be
|
||||
* implemented based on generated public API because we need to access a
|
||||
* map field as a list in reflection API but the generated API only allows
|
||||
* us to access it as a map. This method returns the underlying map field
|
||||
* directly and thus enables us to access the map field as a list.
|
||||
* directly and thus enables us to access the map field as a list.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "rawtypes"})
|
||||
protected MapField internalGetMapField(int fieldNumber) {
|
||||
|
@ -683,7 +683,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
public final <Type> Type getExtension(
|
||||
final ExtensionLite<MessageType, Type> extensionLite) {
|
||||
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
|
||||
|
||||
|
||||
verifyExtensionContainingType(extension);
|
||||
FieldDescriptor descriptor = extension.getDescriptor();
|
||||
final Object value = extensions.getField(descriptor);
|
||||
|
@ -1313,7 +1313,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
implements ExtensionDescriptorRetriever {
|
||||
private volatile FieldDescriptor descriptor;
|
||||
protected abstract FieldDescriptor loadDescriptor();
|
||||
|
||||
|
||||
public FieldDescriptor getDescriptor() {
|
||||
if (descriptor == null) {
|
||||
synchronized (this) {
|
||||
|
@ -1651,17 +1651,17 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the map field with the given field number. This method should be
|
||||
* overridden in the generated message class if the message contains map
|
||||
* fields.
|
||||
*
|
||||
*
|
||||
* Unlike other field types, reflection support for map fields can't be
|
||||
* implemented based on generated public API because we need to access a
|
||||
* map field as a list in reflection API but the generated API only allows
|
||||
* us to access it as a map. This method returns the underlying map field
|
||||
* directly and thus enables us to access the map field as a list.
|
||||
* directly and thus enables us to access the map field as a list.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unused"})
|
||||
protected MapField internalGetMapField(int fieldNumber) {
|
||||
|
@ -1709,7 +1709,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
oneofs = new OneofAccessor[descriptor.getOneofs().size()];
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
|
||||
private boolean isMapFieldEnabled(FieldDescriptor field) {
|
||||
boolean result = true;
|
||||
return result;
|
||||
|
@ -1934,11 +1934,11 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
protected final FieldDescriptor field;
|
||||
protected final boolean isOneofField;
|
||||
protected final boolean hasHasMethod;
|
||||
|
||||
|
||||
private int getOneofFieldNumber(final GeneratedMessage message) {
|
||||
return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber();
|
||||
}
|
||||
|
||||
|
||||
private int getOneofFieldNumber(final GeneratedMessage.Builder builder) {
|
||||
return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
|
||||
}
|
||||
|
@ -2130,15 +2130,15 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
|
||||
private final FieldDescriptor field;
|
||||
private final Message mapEntryMessageDefaultInstance;
|
||||
|
||||
|
||||
private MapField<?, ?> getMapField(GeneratedMessage message) {
|
||||
return (MapField<?, ?>) message.internalGetMapField(field.getNumber());
|
||||
}
|
||||
|
||||
|
||||
private MapField<?, ?> getMapField(GeneratedMessage.Builder builder) {
|
||||
return (MapField<?, ?>) builder.internalGetMapField(field.getNumber());
|
||||
}
|
||||
|
||||
|
||||
public Object get(GeneratedMessage message) {
|
||||
List result = new ArrayList();
|
||||
for (int i = 0; i < getRepeatedCount(message); i++) {
|
||||
|
@ -2171,10 +2171,12 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
}
|
||||
|
||||
public void setRepeated(Builder builder, int index, Object value) {
|
||||
builder.onChanged();
|
||||
getMapField(builder).getMutableList().set(index, (Message) value);
|
||||
}
|
||||
|
||||
public void addRepeated(Builder builder, Object value) {
|
||||
builder.onChanged();
|
||||
getMapField(builder).getMutableList().add((Message) value);
|
||||
}
|
||||
|
||||
|
@ -2197,6 +2199,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
}
|
||||
|
||||
public void clear(Builder builder) {
|
||||
builder.onChanged();
|
||||
getMapField(builder).getMutableList().clear();
|
||||
}
|
||||
|
||||
|
@ -2208,7 +2211,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
throw new UnsupportedOperationException(
|
||||
"Nested builder not supported for map fields.");
|
||||
}
|
||||
|
||||
|
||||
public com.google.protobuf.Message.Builder getRepeatedBuilder(
|
||||
Builder builder, int index) {
|
||||
throw new UnsupportedOperationException(
|
||||
|
@ -2226,7 +2229,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
final Class<? extends Builder> builderClass,
|
||||
final String containingOneofCamelCaseName) {
|
||||
super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
|
||||
|
||||
|
||||
enumDescriptor = descriptor.getEnumType();
|
||||
|
||||
valueOfMethod = getMethodOrDie(type, "valueOf",
|
||||
|
@ -2244,12 +2247,12 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private EnumDescriptor enumDescriptor;
|
||||
|
||||
private Method valueOfMethod;
|
||||
private Method getValueDescriptorMethod;
|
||||
|
||||
|
||||
private boolean supportUnknownEnumValue;
|
||||
private Method getValueMethod;
|
||||
private Method getValueMethodBuilder;
|
||||
|
@ -2291,7 +2294,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
final Class<? extends GeneratedMessage> messageClass,
|
||||
final Class<? extends Builder> builderClass) {
|
||||
super(descriptor, camelCaseName, messageClass, builderClass);
|
||||
|
||||
|
||||
enumDescriptor = descriptor.getEnumType();
|
||||
|
||||
valueOfMethod = getMethodOrDie(type, "valueOf",
|
||||
|
@ -2315,7 +2318,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
|
||||
private final Method valueOfMethod;
|
||||
private final Method getValueDescriptorMethod;
|
||||
|
||||
|
||||
private boolean supportUnknownEnumValue;
|
||||
private Method getRepeatedValueMethod;
|
||||
private Method getRepeatedValueMethodBuilder;
|
||||
|
@ -2395,7 +2398,8 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
final Class<? extends GeneratedMessage> messageClass,
|
||||
final Class<? extends Builder> builderClass,
|
||||
final String containingOneofCamelCaseName) {
|
||||
super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
|
||||
super(descriptor, camelCaseName, messageClass, builderClass,
|
||||
containingOneofCamelCaseName);
|
||||
|
||||
newBuilderMethod = getMethodOrDie(type, "newBuilder");
|
||||
getBuilderMethodBuilder =
|
||||
|
@ -2492,7 +2496,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
protected Object writeReplace() throws ObjectStreamException {
|
||||
return new GeneratedMessageLite.SerializedForm(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that the {@link Extension} is non-Lite and returns it as a
|
||||
* {@link GeneratedExtension}.
|
||||
|
@ -2503,7 +2507,7 @@ public abstract class GeneratedMessage extends AbstractMessage
|
|||
if (extension.isLite()) {
|
||||
throw new IllegalArgumentException("Expected non-lite extension.");
|
||||
}
|
||||
|
||||
|
||||
return (Extension<MessageType, T>) extension;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,22 +57,22 @@ public class MapTest extends TestCase {
|
|||
builder.getMutableInt32ToStringField().put(1, "11");
|
||||
builder.getMutableInt32ToStringField().put(2, "22");
|
||||
builder.getMutableInt32ToStringField().put(3, "33");
|
||||
|
||||
|
||||
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
|
||||
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
|
||||
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
|
||||
|
||||
|
||||
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
|
||||
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
|
||||
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
|
||||
|
||||
|
||||
builder.getMutableInt32ToMessageField().put(
|
||||
1, MessageValue.newBuilder().setValue(11).build());
|
||||
builder.getMutableInt32ToMessageField().put(
|
||||
2, MessageValue.newBuilder().setValue(22).build());
|
||||
builder.getMutableInt32ToMessageField().put(
|
||||
3, MessageValue.newBuilder().setValue(33).build());
|
||||
|
||||
|
||||
builder.getMutableStringToInt32Field().put("1", 11);
|
||||
builder.getMutableStringToInt32Field().put("2", 22);
|
||||
builder.getMutableStringToInt32Field().put("3", 33);
|
||||
|
@ -88,22 +88,22 @@ public class MapTest extends TestCase {
|
|||
assertEquals("11", message.getInt32ToStringField().get(1));
|
||||
assertEquals("22", message.getInt32ToStringField().get(2));
|
||||
assertEquals("33", message.getInt32ToStringField().get(3));
|
||||
|
||||
|
||||
assertEquals(3, message.getInt32ToBytesField().size());
|
||||
assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
|
||||
assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
|
||||
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
|
||||
|
||||
|
||||
assertEquals(3, message.getInt32ToEnumField().size());
|
||||
assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
|
||||
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
|
||||
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
|
||||
|
||||
|
||||
assertEquals(3, message.getInt32ToMessageField().size());
|
||||
assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
|
||||
assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
|
||||
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
|
||||
|
||||
|
||||
assertEquals(3, message.getStringToInt32Field().size());
|
||||
assertEquals(11, message.getStringToInt32Field().get("1").intValue());
|
||||
assertEquals(22, message.getStringToInt32Field().get("2").intValue());
|
||||
|
@ -118,21 +118,21 @@ public class MapTest extends TestCase {
|
|||
builder.getMutableInt32ToStringField().put(1, "111");
|
||||
builder.getMutableInt32ToStringField().remove(2);
|
||||
builder.getMutableInt32ToStringField().put(4, "44");
|
||||
|
||||
|
||||
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
|
||||
builder.getMutableInt32ToBytesField().remove(2);
|
||||
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
|
||||
|
||||
|
||||
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
|
||||
builder.getMutableInt32ToEnumField().remove(2);
|
||||
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
|
||||
|
||||
|
||||
builder.getMutableInt32ToMessageField().put(
|
||||
1, MessageValue.newBuilder().setValue(111).build());
|
||||
builder.getMutableInt32ToMessageField().remove(2);
|
||||
builder.getMutableInt32ToMessageField().put(
|
||||
4, MessageValue.newBuilder().setValue(44).build());
|
||||
|
||||
|
||||
builder.getMutableStringToInt32Field().put("1", 111);
|
||||
builder.getMutableStringToInt32Field().remove("2");
|
||||
builder.getMutableStringToInt32Field().put("4", 44);
|
||||
|
@ -148,22 +148,22 @@ public class MapTest extends TestCase {
|
|||
assertEquals("111", message.getInt32ToStringField().get(1));
|
||||
assertEquals("33", message.getInt32ToStringField().get(3));
|
||||
assertEquals("44", message.getInt32ToStringField().get(4));
|
||||
|
||||
|
||||
assertEquals(3, message.getInt32ToBytesField().size());
|
||||
assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
|
||||
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
|
||||
assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
|
||||
|
||||
|
||||
assertEquals(3, message.getInt32ToEnumField().size());
|
||||
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
|
||||
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
|
||||
assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
|
||||
|
||||
|
||||
assertEquals(3, message.getInt32ToMessageField().size());
|
||||
assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
|
||||
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
|
||||
assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
|
||||
|
||||
|
||||
assertEquals(3, message.getStringToInt32Field().size());
|
||||
assertEquals(111, message.getStringToInt32Field().get("1").intValue());
|
||||
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
|
||||
|
@ -183,17 +183,17 @@ public class MapTest extends TestCase {
|
|||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
TestMap message = builder.build();
|
||||
assertMapValuesCleared(message);
|
||||
|
||||
|
||||
builder = message.toBuilder();
|
||||
setMapValues(builder);
|
||||
message = builder.build();
|
||||
assertMapValuesSet(message);
|
||||
|
||||
|
||||
builder = message.toBuilder();
|
||||
updateMapValues(builder);
|
||||
message = builder.build();
|
||||
assertMapValuesUpdated(message);
|
||||
|
||||
|
||||
builder = message.toBuilder();
|
||||
builder.clear();
|
||||
message = builder.build();
|
||||
|
@ -207,14 +207,14 @@ public class MapTest extends TestCase {
|
|||
assertEquals(message.getSerializedSize(), message.toByteString().size());
|
||||
message = TestMap.PARSER.parseFrom(message.toByteString());
|
||||
assertMapValuesSet(message);
|
||||
|
||||
|
||||
builder = message.toBuilder();
|
||||
updateMapValues(builder);
|
||||
message = builder.build();
|
||||
assertEquals(message.getSerializedSize(), message.toByteString().size());
|
||||
message = TestMap.PARSER.parseFrom(message.toByteString());
|
||||
assertMapValuesUpdated(message);
|
||||
|
||||
|
||||
builder = message.toBuilder();
|
||||
builder.clear();
|
||||
message = builder.build();
|
||||
|
@ -222,12 +222,12 @@ public class MapTest extends TestCase {
|
|||
message = TestMap.PARSER.parseFrom(message.toByteString());
|
||||
assertMapValuesCleared(message);
|
||||
}
|
||||
|
||||
|
||||
public void testMergeFrom() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
setMapValues(builder);
|
||||
TestMap message = builder.build();
|
||||
|
||||
|
||||
TestMap.Builder other = TestMap.newBuilder();
|
||||
other.mergeFrom(message);
|
||||
assertMapValuesSet(other.build());
|
||||
|
@ -236,7 +236,7 @@ public class MapTest extends TestCase {
|
|||
public void testEqualsAndHashCode() throws Exception {
|
||||
// Test that generated equals() and hashCode() will disregard the order
|
||||
// of map entries when comparing/hashing map fields.
|
||||
|
||||
|
||||
// We can't control the order of elements in a HashMap. The best we can do
|
||||
// here is to add elements in different order.
|
||||
TestMap.Builder b1 = TestMap.newBuilder();
|
||||
|
@ -244,23 +244,23 @@ public class MapTest extends TestCase {
|
|||
b1.getMutableInt32ToInt32Field().put(3, 4);
|
||||
b1.getMutableInt32ToInt32Field().put(5, 6);
|
||||
TestMap m1 = b1.build();
|
||||
|
||||
|
||||
TestMap.Builder b2 = TestMap.newBuilder();
|
||||
b2.getMutableInt32ToInt32Field().put(5, 6);
|
||||
b2.getMutableInt32ToInt32Field().put(1, 2);
|
||||
b2.getMutableInt32ToInt32Field().put(3, 4);
|
||||
TestMap m2 = b2.build();
|
||||
|
||||
|
||||
assertEquals(m1, m2);
|
||||
assertEquals(m1.hashCode(), m2.hashCode());
|
||||
|
||||
|
||||
// Make sure we did compare map fields.
|
||||
b2.getMutableInt32ToInt32Field().put(1, 0);
|
||||
m2 = b2.build();
|
||||
assertFalse(m1.equals(m2));
|
||||
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
|
||||
// to be different.
|
||||
|
||||
|
||||
// Regression test for b/18549190: if a map is a subset of the other map,
|
||||
// equals() should return false.
|
||||
b2.getMutableInt32ToInt32Field().remove(1);
|
||||
|
@ -268,57 +268,96 @@ public class MapTest extends TestCase {
|
|||
assertFalse(m1.equals(m2));
|
||||
assertFalse(m2.equals(m1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void testNestedBuilderOnChangeEventPropagation() {
|
||||
TestOnChangeEventPropagation.Builder parent =
|
||||
TestOnChangeEventPropagation.newBuilder();
|
||||
parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2);
|
||||
TestOnChangeEventPropagation message = parent.build();
|
||||
assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
|
||||
|
||||
|
||||
// Make a change using nested builder.
|
||||
parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3);
|
||||
|
||||
|
||||
// Should be able to observe the change.
|
||||
message = parent.build();
|
||||
assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
|
||||
|
||||
|
||||
// Make another change using mergeFrom()
|
||||
TestMap.Builder other = TestMap.newBuilder();
|
||||
other.getMutableInt32ToInt32Field().put(1, 4);
|
||||
parent.getOptionalMessageBuilder().mergeFrom(other.build());
|
||||
|
||||
|
||||
// Should be able to observe the change.
|
||||
message = parent.build();
|
||||
assertEquals(4, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
|
||||
|
||||
|
||||
// Make yet another change by clearing the nested builder.
|
||||
parent.getOptionalMessageBuilder().clear();
|
||||
|
||||
|
||||
// Should be able to observe the change.
|
||||
message = parent.build();
|
||||
assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size());
|
||||
}
|
||||
|
||||
|
||||
public void testNestedBuilderOnChangeEventPropagationReflection() {
|
||||
FieldDescriptor intMapField = f("int32_to_int32_field");
|
||||
// Create an outer message builder with nested builder.
|
||||
TestOnChangeEventPropagation.Builder parentBuilder =
|
||||
TestOnChangeEventPropagation.newBuilder();
|
||||
TestMap.Builder testMapBuilder = parentBuilder.getOptionalMessageBuilder();
|
||||
|
||||
// Create a map entry message.
|
||||
TestMap.Builder entryBuilder = TestMap.newBuilder();
|
||||
entryBuilder.getMutableInt32ToInt32Field().put(1, 1);
|
||||
|
||||
// Put the entry into the nested builder.
|
||||
testMapBuilder.addRepeatedField(
|
||||
intMapField, entryBuilder.getRepeatedField(intMapField, 0));
|
||||
|
||||
// Should be able to observe the change.
|
||||
TestOnChangeEventPropagation message = parentBuilder.build();
|
||||
assertEquals(1, message.getOptionalMessage().getInt32ToInt32Field().size());
|
||||
|
||||
// Change the entry value.
|
||||
entryBuilder.getMutableInt32ToInt32Field().put(1, 4);
|
||||
testMapBuilder = parentBuilder.getOptionalMessageBuilder();
|
||||
testMapBuilder.setRepeatedField(
|
||||
intMapField, 0, entryBuilder.getRepeatedField(intMapField, 0));
|
||||
|
||||
// Should be able to observe the change.
|
||||
message = parentBuilder.build();
|
||||
assertEquals(4,
|
||||
message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
|
||||
|
||||
// Clear the nested builder.
|
||||
testMapBuilder = parentBuilder.getOptionalMessageBuilder();
|
||||
testMapBuilder.clearField(intMapField);
|
||||
|
||||
// Should be able to observe the change.
|
||||
message = parentBuilder.build();
|
||||
assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size());
|
||||
}
|
||||
|
||||
// The following methods are used to test reflection API.
|
||||
|
||||
|
||||
private static FieldDescriptor f(String name) {
|
||||
return TestMap.getDescriptor().findFieldByName(name);
|
||||
}
|
||||
|
||||
|
||||
private static Object getFieldValue(Message mapEntry, String name) {
|
||||
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
|
||||
return mapEntry.getField(field);
|
||||
}
|
||||
|
||||
|
||||
private static Message.Builder setFieldValue(
|
||||
Message.Builder mapEntry, String name, Object value) {
|
||||
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
|
||||
mapEntry.setField(field, value);
|
||||
return mapEntry;
|
||||
}
|
||||
|
||||
|
||||
private static void assertHasMapValues(Message message, String name, Map<?, ?> values) {
|
||||
FieldDescriptor field = f(name);
|
||||
for (Object entry : (List<?>) message.getField(field)) {
|
||||
|
@ -337,7 +376,7 @@ public class MapTest extends TestCase {
|
|||
assertEquals(value, values.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static <KeyType, ValueType>
|
||||
Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) {
|
||||
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
|
||||
|
@ -348,7 +387,7 @@ public class MapTest extends TestCase {
|
|||
entryBuilder.setField(valueField, value);
|
||||
return entryBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) {
|
||||
List<Message> entryList = new ArrayList<Message>();
|
||||
for (Map.Entry<?, ?> entry : values.entrySet()) {
|
||||
|
@ -357,7 +396,7 @@ public class MapTest extends TestCase {
|
|||
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
|
||||
builder.setField(field, entryList);
|
||||
}
|
||||
|
||||
|
||||
private static <KeyType, ValueType>
|
||||
Map<KeyType, ValueType> mapForValues(
|
||||
KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
|
||||
|
@ -385,14 +424,14 @@ public class MapTest extends TestCase {
|
|||
mapForValues(
|
||||
11, MessageValue.newBuilder().setValue(22).build(),
|
||||
33, MessageValue.newBuilder().setValue(44).build()));
|
||||
|
||||
|
||||
// Test clearField()
|
||||
builder.clearField(f("int32_to_int32_field"));
|
||||
builder.clearField(f("int32_to_message_field"));
|
||||
message = builder.build();
|
||||
assertEquals(0, message.getInt32ToInt32Field().size());
|
||||
assertEquals(0, message.getInt32ToMessageField().size());
|
||||
|
||||
|
||||
// Test setField()
|
||||
setMapValues(builder, "int32_to_int32_field",
|
||||
mapForValues(11, 22, 33, 44));
|
||||
|
@ -405,7 +444,7 @@ public class MapTest extends TestCase {
|
|||
assertEquals(44, message.getInt32ToInt32Field().get(33).intValue());
|
||||
assertEquals(222, message.getInt32ToMessageField().get(111).getValue());
|
||||
assertEquals(444, message.getInt32ToMessageField().get(333).getValue());
|
||||
|
||||
|
||||
// Test addRepeatedField
|
||||
builder.addRepeatedField(f("int32_to_int32_field"),
|
||||
newMapEntry(builder, "int32_to_int32_field", 55, 66));
|
||||
|
@ -425,7 +464,7 @@ public class MapTest extends TestCase {
|
|||
message = builder.build();
|
||||
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
|
||||
assertEquals(555, message.getInt32ToMessageField().get(555).getValue());
|
||||
|
||||
|
||||
// Test setRepeatedField
|
||||
for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) {
|
||||
Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i);
|
||||
|
@ -442,35 +481,35 @@ public class MapTest extends TestCase {
|
|||
assertEquals(33, message.getInt32ToInt32Field().get(44).intValue());
|
||||
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
|
||||
}
|
||||
|
||||
|
||||
public void testTextFormat() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
setMapValues(builder);
|
||||
TestMap message = builder.build();
|
||||
|
||||
|
||||
String textData = TextFormat.printToString(message);
|
||||
|
||||
|
||||
builder = TestMap.newBuilder();
|
||||
TextFormat.merge(textData, builder);
|
||||
message = builder.build();
|
||||
|
||||
|
||||
assertMapValuesSet(message);
|
||||
}
|
||||
|
||||
|
||||
public void testDynamicMessage() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
setMapValues(builder);
|
||||
TestMap message = builder.build();
|
||||
|
||||
|
||||
Message dynamicDefaultInstance =
|
||||
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
|
||||
Message dynamicMessage = dynamicDefaultInstance
|
||||
.newBuilderForType().mergeFrom(message.toByteString()).build();
|
||||
|
||||
|
||||
assertEquals(message, dynamicMessage);
|
||||
assertEquals(message.hashCode(), dynamicMessage.hashCode());
|
||||
}
|
||||
|
||||
|
||||
public void testReflectionEqualsAndHashCode() throws Exception {
|
||||
// Test that generated equals() and hashCode() will disregard the order
|
||||
// of map entries when comparing/hashing map fields.
|
||||
|
@ -479,22 +518,22 @@ public class MapTest extends TestCase {
|
|||
Message dynamicDefaultInstance =
|
||||
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
|
||||
FieldDescriptor field = f("int32_to_int32_field");
|
||||
|
||||
|
||||
Message.Builder b1 = dynamicDefaultInstance.newBuilderForType();
|
||||
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2));
|
||||
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4));
|
||||
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6));
|
||||
Message m1 = b1.build();
|
||||
|
||||
|
||||
Message.Builder b2 = dynamicDefaultInstance.newBuilderForType();
|
||||
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6));
|
||||
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2));
|
||||
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4));
|
||||
Message m2 = b2.build();
|
||||
|
||||
|
||||
assertEquals(m1, m2);
|
||||
assertEquals(m1.hashCode(), m2.hashCode());
|
||||
|
||||
|
||||
// Make sure we did compare map fields.
|
||||
b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0));
|
||||
m2 = b2.build();
|
||||
|
@ -502,7 +541,7 @@ public class MapTest extends TestCase {
|
|||
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
|
||||
// to be different.
|
||||
}
|
||||
|
||||
|
||||
public void testUnknownEnumValues() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
builder.getMutableInt32ToEnumFieldValue().put(0, 0);
|
||||
|
@ -517,7 +556,7 @@ public class MapTest extends TestCase {
|
|||
assertEquals(TestMap.EnumValue.UNRECOGNIZED,
|
||||
message.getInt32ToEnumField().get(2));
|
||||
assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue());
|
||||
|
||||
|
||||
// Unknown enum values should be preserved after:
|
||||
// 1. Serialization and parsing.
|
||||
// 2. toBuild().
|
||||
|
@ -528,7 +567,7 @@ public class MapTest extends TestCase {
|
|||
assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
|
||||
builder = TestMap.newBuilder().mergeFrom(message);
|
||||
assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
|
||||
|
||||
|
||||
// hashCode()/equals() should take unknown enum values into account.
|
||||
builder.getMutableInt32ToEnumFieldValue().put(2, 1001);
|
||||
TestMap message2 = builder.build();
|
||||
|
@ -538,17 +577,17 @@ public class MapTest extends TestCase {
|
|||
// should be the same.
|
||||
assertTrue(message.getInt32ToEnumField().equals(message2.getInt32ToEnumField()));
|
||||
}
|
||||
|
||||
|
||||
public void testUnknownEnumValuesInReflectionApi() throws Exception {
|
||||
Descriptor descriptor = TestMap.getDescriptor();
|
||||
EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor();
|
||||
FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field");
|
||||
|
||||
|
||||
Map<Integer, Integer> data = new HashMap<Integer, Integer>();
|
||||
data.put(0, 0);
|
||||
data.put(1, 1);
|
||||
data.put(2, 1000); // unknown value.
|
||||
|
||||
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
for (Map.Entry<Integer, Integer> entry : data.entrySet()) {
|
||||
builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue());
|
||||
|
|
Loading…
Add table
Reference in a new issue