aboutsummaryrefslogtreecommitdiff
path: root/java/util
diff options
context:
space:
mode:
Diffstat (limited to 'java/util')
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/Durations.java3
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java16
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java2
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/JsonFormat.java2
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java150
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java12
6 files changed, 132 insertions, 53 deletions
diff --git a/java/util/src/main/java/com/google/protobuf/util/Durations.java b/java/util/src/main/java/com/google/protobuf/util/Durations.java
index fb7f4343..17b41cbb 100644
--- a/java/util/src/main/java/com/google/protobuf/util/Durations.java
+++ b/java/util/src/main/java/com/google/protobuf/util/Durations.java
@@ -61,6 +61,9 @@ public final class Durations {
public static final Duration MAX_VALUE =
Duration.newBuilder().setSeconds(DURATION_SECONDS_MAX).setNanos(999999999).build();
+ /** A constant holding the duration of zero. */
+ public static final Duration ZERO = Duration.newBuilder().setSeconds(0L).setNanos(0).build();
+
private Durations() {}
private static final Comparator<Duration> COMPARATOR =
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
index 429579fc..86f56ad9 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
@@ -249,12 +249,9 @@ final class FieldMaskTree {
continue;
}
String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
- merge(
- entry.getValue(),
- childPath,
- (Message) source.getField(field),
- destination.getFieldBuilder(field),
- options);
+ Message.Builder childBuilder = ((Message) destination.getField(field)).toBuilder();
+ merge(entry.getValue(), childPath, (Message) source.getField(field), childBuilder, options);
+ destination.setField(field, childBuilder.buildPartial());
continue;
}
if (field.isRepeated()) {
@@ -275,7 +272,12 @@ final class FieldMaskTree {
}
} else {
if (source.hasField(field)) {
- destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
+ destination.setField(
+ field,
+ ((Message) destination.getField(field))
+ .toBuilder()
+ .mergeFrom((Message) source.getField(field))
+ .build());
}
}
} else {
diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
index b2f849c4..aedc5eac 100644
--- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
@@ -235,7 +235,7 @@ public class FieldMaskUtil {
/**
* Converts a FieldMask to its canonical form. In the canonical form of a
* FieldMask, all field paths are sorted alphabetically and redundant field
- * paths are moved.
+ * paths are removed.
*/
public static FieldMask normalize(FieldMask mask) {
return new FieldMaskTree(mask).toFieldMask();
diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
index a1e2d9c0..955dfd86 100644
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -611,7 +611,7 @@ public class JsonFormat {
private final CharSequence blankOrNewLine;
private static class GsonHolder {
- private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
+ private static final Gson DEFAULT_GSON = new GsonBuilder().create();
}
PrinterImpl(
diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
index 853b6151..a0d317d4 100644
--- a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
@@ -30,9 +30,14 @@
package com.google.protobuf.util;
+import com.google.protobuf.DynamicMessage;
+import com.google.protobuf.Message;
+import com.google.protobuf.UninitializedMessageException;
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
+import protobuf_unittest.UnittestProto.TestRequired;
+import protobuf_unittest.UnittestProto.TestRequiredMessage;
import junit.framework.TestCase;
public class FieldMaskTreeTest extends TestCase {
@@ -90,8 +95,68 @@ public class FieldMaskTreeTest extends TestCase {
tree.intersectFieldPath("bar", result);
assertEquals("bar.baz,bar.quz,foo", result.toString());
}
-
+
public void testMerge() throws Exception {
+ testMergeImpl(true);
+ testMergeImpl(false);
+ testMergeRequire(false);
+ testMergeRequire(true);
+ }
+
+ private void merge(
+ FieldMaskTree tree,
+ Message source,
+ Message.Builder builder,
+ FieldMaskUtil.MergeOptions options,
+ boolean useDynamicMessage)
+ throws Exception {
+ if (useDynamicMessage) {
+ Message.Builder newBuilder =
+ DynamicMessage.newBuilder(source.getDescriptorForType())
+ .mergeFrom(builder.buildPartial().toByteArray());
+ tree.merge(
+ DynamicMessage.newBuilder(source.getDescriptorForType())
+ .mergeFrom(source.toByteArray())
+ .build(),
+ newBuilder,
+ options);
+ builder.clear();
+ builder.mergeFrom(newBuilder.buildPartial());
+ } else {
+ tree.merge(source, builder, options);
+ }
+ }
+
+ private void testMergeRequire(boolean useDynamicMessage) throws Exception {
+ TestRequired value = TestRequired.newBuilder().setA(4321).setB(8765).setC(233333).build();
+ TestRequiredMessage source = TestRequiredMessage.newBuilder().setRequiredMessage(value).build();
+
+ FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
+ TestRequiredMessage.Builder builder = TestRequiredMessage.newBuilder();
+ merge(
+ new FieldMaskTree().addFieldPath("required_message.a"),
+ source,
+ builder,
+ options,
+ useDynamicMessage);
+ assertTrue(builder.hasRequiredMessage());
+ assertTrue(builder.getRequiredMessage().hasA());
+ assertFalse(builder.getRequiredMessage().hasB());
+ assertFalse(builder.getRequiredMessage().hasC());
+ merge(
+ new FieldMaskTree().addFieldPath("required_message.b").addFieldPath("required_message.c"),
+ source,
+ builder,
+ options,
+ useDynamicMessage);
+ try {
+ assertEquals(builder.build(), source);
+ } catch (UninitializedMessageException e) {
+ throw new AssertionError("required field isn't set", e);
+ }
+ }
+
+ private void testMergeImpl(boolean useDynamicMessage) throws Exception {
TestAllTypes value =
TestAllTypes.newBuilder()
.setOptionalInt32(1234)
@@ -119,45 +184,51 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging each individual field.
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.optional_int32").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
+ source, builder, options, useDynamicMessage);
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("payload.optional_nested_message")
- .merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_nested_message"),
+ source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
+ source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedInt32(4321);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("payload.repeated_nested_message")
- .merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.repeated_nested_message"),
+ source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("child.payload.optional_int32")
- .merge(source, builder, options);
+ merge(
+ new FieldMaskTree().addFieldPath("child.payload.optional_int32"),
+ source,
+ builder,
+ options,
+ useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("child.payload.optional_nested_message")
- .merge(source, builder, options);
+ merge(
+ new FieldMaskTree().addFieldPath("child.payload.optional_nested_message"),
+ source,
+ builder,
+ options,
+ useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected
.getChildBuilder()
@@ -166,17 +237,15 @@ public class FieldMaskTreeTest extends TestCase {
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("child.payload.repeated_int32")
- .merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("child.payload.repeated_int32"),
+ source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("child.payload.repeated_nested_message")
- .merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message"),
+ source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected
.getChildBuilder()
@@ -186,23 +255,23 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging all fields.
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("child")
- .addFieldPath("payload")
- .merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("child").addFieldPath("payload"),
+ source, builder, options, useDynamicMessage);
assertEquals(source, builder.build());
// Test repeated options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().addRepeatedInt32(1000);
- new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
+ source, builder, options, useDynamicMessage);
// Default behavior is to append repeated fields.
assertEquals(2, builder.getPayload().getRepeatedInt32Count());
assertEquals(1000, builder.getPayload().getRepeatedInt32(0));
assertEquals(4321, builder.getPayload().getRepeatedInt32(1));
// Change to replace repeated fields.
options.setReplaceRepeatedFields(true);
- new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
+ source, builder, options, useDynamicMessage);
assertEquals(1, builder.getPayload().getRepeatedInt32Count());
assertEquals(4321, builder.getPayload().getRepeatedInt32(0));
@@ -210,7 +279,8 @@ public class FieldMaskTreeTest extends TestCase {
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
- new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload"),
+ source, builder, options, useDynamicMessage);
// Default behavior is to merge message fields.
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(2000, builder.getPayload().getOptionalUint32());
@@ -218,14 +288,14 @@ public class FieldMaskTreeTest extends TestCase {
// Test merging unset message fields.
NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload"),
+ clearedSource, builder, options, useDynamicMessage);
assertEquals(false, builder.hasPayload());
// Skip a message field if they are unset in both source and target.
builder = NestedTestAllTypes.newBuilder();
- new FieldMaskTree()
- .addFieldPath("payload.optional_int32")
- .merge(clearedSource, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
+ clearedSource, builder, options, useDynamicMessage);
assertEquals(false, builder.hasPayload());
// Change to replace message fields.
@@ -233,7 +303,8 @@ public class FieldMaskTreeTest extends TestCase {
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
- new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload"),
+ source, builder, options, useDynamicMessage);
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(0, builder.getPayload().getOptionalUint32());
@@ -241,7 +312,8 @@ public class FieldMaskTreeTest extends TestCase {
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
- new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload"),
+ clearedSource, builder, options, useDynamicMessage);
assertEquals(false, builder.hasPayload());
// Test merging unset primitive fields.
@@ -249,18 +321,16 @@ public class FieldMaskTreeTest extends TestCase {
builder.getPayloadBuilder().clearOptionalInt32();
NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build();
builder = source.toBuilder();
- new FieldMaskTree()
- .addFieldPath("payload.optional_int32")
- .merge(sourceWithPayloadInt32Unset, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
+ sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
assertEquals(true, builder.getPayload().hasOptionalInt32());
assertEquals(0, builder.getPayload().getOptionalInt32());
// Change to clear unset primitive fields.
options.setReplacePrimitiveFields(true);
builder = source.toBuilder();
- new FieldMaskTree()
- .addFieldPath("payload.optional_int32")
- .merge(sourceWithPayloadInt32Unset, builder, options);
+ merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
+ sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
assertEquals(true, builder.hasPayload());
assertEquals(false, builder.getPayload().hasOptionalInt32());
}
diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
index ede8de7a..7637c267 100644
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -1236,10 +1236,14 @@ public class JsonFormatTest extends TestCase {
assertRoundTripEquals(message);
}
- public void testDefaultGsonDoesNotHtmlEscape() throws Exception {
- TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("=").build();
- assertEquals(
- "{\n" + " \"optionalString\": \"=\"" + "\n}", JsonFormat.printer().print(message));
+ // Regression test for b/73832901. Make sure html tags are escaped.
+ public void testHtmlEscape() throws Exception {
+ TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("</script>").build();
+ assertEquals("{\n \"optionalString\": \"\\u003c/script\\u003e\"\n}", toJsonString(message));
+
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ JsonFormat.parser().merge(toJsonString(message), builder);
+ assertEquals(message.getOptionalString(), builder.getOptionalString());
}
public void testIncludingDefaultValueFields() throws Exception {