Browse Source

Fix the behavior when merging conflicting keys, the new value always
override the existing one even for message types.

Jisi Liu 11 năm trước cách đây
mục cha
commit
bd3573cb09

+ 7 - 33
javanano/src/main/java/com/google/protobuf/nano/InternalNano.java

@@ -391,12 +391,12 @@ public final class InternalNano {
    * be called by generated messages.
    *
    * @param map the map field; may be null, in which case a map will be
-   *        instantiated using the {@link MapUtil.MapFactory}
+   *        instantiated using the {@link MapFactories.MapFactory}
    * @param input the input byte buffer
    * @param keyType key type, as defined in InternalNano.TYPE_*
    * @param valueType value type, as defined in InternalNano.TYPE_*
-   * @param valueClazz class of the value field if the valueType is
-   *        TYPE_MESSAGE; otherwise the parameter is ignored and can be null.
+   * @param value an new instance of the value, if the value is a TYPE_MESSAGE;
+   *        otherwise this parameter can be null and will be ignored.
    * @param keyTag wire tag for the key
    * @param valueTag wire tag for the value
    * @return the map field
@@ -408,15 +408,13 @@ public final class InternalNano {
       Map<K, V> map,
       int keyType,
       int valueType,
-      Class<V> valueClazz,
+      V value,
       int keyTag,
       int valueTag) throws IOException {
     map = MapFactories.getMapFactory().forMap(map);
     final int length = input.readRawVarint32();
     final int oldLimit = input.pushLimit(length);
-    byte[] payload = null;
     K key = null;
-    V value = null;
     while (true) {
       int tag = input.readTag();
       if (tag == 0) {
@@ -426,7 +424,7 @@ public final class InternalNano {
         key = (K) input.readData(keyType);
       } else if (tag == valueTag) {
         if (valueType == TYPE_MESSAGE) {
-          payload = input.readBytes();
+          input.readMessage((MessageNano) value);
         } else {
           value = (V) input.readData(valueType);
         }
@@ -440,36 +438,12 @@ public final class InternalNano {
     input.popLimit(oldLimit);
 
     if (key == null) {
+      // key can only be primitive types.
       key = (K) primitiveDefaultValue(keyType);
     }
 
-    // Special case: merge the value when the value is a message.
-    if (valueType == TYPE_MESSAGE) {
-      MessageNano oldMessageValue = (MessageNano) map.get(key);
-      if (oldMessageValue != null) {
-        if (payload != null) {
-          MessageNano.mergeFrom(oldMessageValue, payload);
-        }
-        return map;
-      }
-      // Otherwise, create a new value message.
-      try {
-        value = valueClazz.newInstance();
-      } catch (InstantiationException e) {
-        throw new IOException(
-            "Unable to create value message " + valueClazz.getName()
-            + " in maps.");
-      } catch (IllegalAccessException e) {
-        throw new IOException(
-            "Unable to create value message " + valueClazz.getName()
-            + " in maps.");
-      }
-      if (payload != null) {
-        MessageNano.mergeFrom((MessageNano) value, payload);
-      }
-    }
-
     if (value == null) {
+      // message type
       value = (V) primitiveDefaultValue(valueType);
     }
 

+ 27 - 1
javanano/src/test/java/com/google/protobuf/nano/NanoTest.java

@@ -3742,7 +3742,6 @@ public class NanoTest extends TestCase {
     byte[] output = MessageNano.toByteArray(origin);
     TestMap parsed = new TestMap();
     MessageNano.mergeFrom(parsed, output);
-    // TODO(liujisi): Test merging message type values.
     // TODO(liujisi): Test missing key/value in parsing.
   }
 
@@ -3769,6 +3768,33 @@ public class NanoTest extends TestCase {
     }
   }
 
+  /**
+   * Tests that merging bytes containing conflicting keys with override the
+   * message value instead of merging the message value into the existing entry.
+   */
+  public void testMapMergeOverrideMessageValues() throws Exception {
+    TestMap.MessageValue origValue = new TestMap.MessageValue();
+    origValue.value = 1;
+    origValue.value2 = 2;
+    TestMap.MessageValue newValue = new TestMap.MessageValue();
+    newValue.value = 3;
+
+    TestMap origMessage = new TestMap();
+    origMessage.int32ToMessageField =
+        new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+    origMessage.int32ToMessageField.put(1, origValue);
+
+    TestMap newMessage = new TestMap();
+    newMessage.int32ToMessageField =
+        new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+    newMessage.int32ToMessageField.put(1, newValue);
+    MessageNano.mergeFrom(origMessage,
+        MessageNano.toByteArray(newMessage));
+    TestMap.MessageValue mergedValue = origMessage.int32ToMessageField.get(1);
+    assertEquals(3, mergedValue.value);
+    assertEquals(0, mergedValue.value2);
+  }
+
   private static final Integer[] int32Values = new Integer[] {
     0, 1, -1, Integer.MAX_VALUE, Integer.MIN_VALUE,
   };

+ 1 - 0
javanano/src/test/java/com/google/protobuf/nano/map_test.proto

@@ -38,6 +38,7 @@ option java_outer_classname = "MapTestProto";
 message TestMap {
   message MessageValue {
     int32 value = 1;
+    int32 value2 = 2;
   }
   enum EnumValue {
     FOO = 0;

+ 3 - 3
src/google/protobuf/compiler/javanano/javanano_map_field.cc

@@ -102,9 +102,9 @@ void SetMapVariables(const Params& params,
   (*variables)["value_tag"] = SimpleItoa(internal::WireFormat::MakeTag(value));
   (*variables)["type_parameters"] =
       (*variables)["boxed_key_type"] + ", " + (*variables)["boxed_value_type"];
-  (*variables)["value_class"] =
+  (*variables)["value_default"] =
       value->type() == FieldDescriptor::TYPE_MESSAGE
-          ? (*variables)["value_type"] + ".class"
+          ? "new " + (*variables)["value_type"] + "()"
           : "null";
 }
 }  // namespace
@@ -137,7 +137,7 @@ GenerateMergingCode(io::Printer* printer) const {
     "  input, this.$name$,\n"
     "  com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
     "  com.google.protobuf.nano.InternalNano.$value_desc_type$,\n"
-    "  $value_class$,\n"
+    "  $value_default$,\n"
     "  $key_tag$, $value_tag$);\n"
     "\n");
 }