aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/util
diff options
context:
space:
mode:
authorGravatar Adam Cozzette <acozzette@google.com>2016-06-29 15:23:27 -0700
committerGravatar Adam Cozzette <acozzette@google.com>2016-06-29 15:38:03 -0700
commitd64a2d9941c36a7bc2a7959ea10ab8363192ac14 (patch)
tree52330d146ad63d3d70f3baade00d5d1fea8f5e0c /java/util
parentc18aa7795a2e02ef700ff8b039d94ecdcc33432f (diff)
Integrated internal changes from Google
This includes all internal changes from around May 20 to now.
Diffstat (limited to 'java/util')
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/Durations.java256
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java48
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java78
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/JsonFormat.java715
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/TimeUtil.java381
-rw-r--r--java/util/src/main/java/com/google/protobuf/util/Timestamps.java349
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java113
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java733
-rw-r--r--java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java77
-rw-r--r--java/util/src/test/proto/com/google/protobuf/util/json_test.proto1
10 files changed, 1602 insertions, 1149 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
new file mode 100644
index 00000000..5fe6ebca
--- /dev/null
+++ b/java/util/src/main/java/com/google/protobuf/util/Durations.java
@@ -0,0 +1,256 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf.util;
+
+import static com.google.protobuf.util.Timestamps.MICROS_PER_SECOND;
+import static com.google.protobuf.util.Timestamps.MILLIS_PER_SECOND;
+import static com.google.protobuf.util.Timestamps.NANOS_PER_MICROSECOND;
+import static com.google.protobuf.util.Timestamps.NANOS_PER_MILLISECOND;
+import static com.google.protobuf.util.Timestamps.NANOS_PER_SECOND;
+
+import com.google.protobuf.Duration;
+
+import java.text.ParseException;
+
+/**
+ * Utilities to help create/manipulate {@code protobuf/duration.proto}.
+ */
+public final class Durations {
+ static final long DURATION_SECONDS_MIN = -315576000000L;
+ static final long DURATION_SECONDS_MAX = 315576000000L;
+
+ // TODO(kak): Do we want to expose Duration constants for MAX/MIN?
+
+ private Durations() {}
+
+ /**
+ * Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the
+ * range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range
+ * [-999,999,999, +999,999,999].
+ *
+ * <p>Note: Durations less than one second are represented with a 0 {@code seconds} field and a
+ * positive or negative {@code nanos} field. For durations of one second or more, a non-zero value
+ * for the {@code nanos} field must be of the same sign as the {@code seconds} field.
+ */
+ public static boolean isValid(Duration duration) {
+ return isValid(duration.getSeconds(), duration.getNanos());
+ }
+
+ /**
+ * Returns true if the given number of seconds and nanos is a valid {@link Duration}. The
+ * {@code seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The
+ * {@code nanos} value must be in the range [-999,999,999, +999,999,999].
+ *
+ * <p>Note: Durations less than one second are represented with a 0 {@code seconds} field and a
+ * positive or negative {@code nanos} field. For durations of one second or more, a non-zero value
+ * for the {@code nanos} field must be of the same sign as the {@code seconds} field.
+ */
+ public static boolean isValid(long seconds, long nanos) {
+ if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) {
+ return false;
+ }
+ if (nanos < -999999999L || nanos >= NANOS_PER_SECOND) {
+ return false;
+ }
+ if (seconds < 0 || nanos < 0) {
+ if (seconds > 0 || nanos > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Throws an {@link IllegalArgumentException} if the given seconds/nanos are not
+ * a valid {@link Duration}.
+ */
+ private static void checkValid(long seconds, int nanos) {
+ if (!isValid(seconds, nanos)) {
+ throw new IllegalArgumentException(String.format(
+ "Duration is not valid. See proto definition for valid values. "
+ + "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]."
+ + "Nanos (%s) must be in range [-999,999,999, +999,999,999]. "
+ + "Nanos must have the same sign as seconds", seconds, nanos));
+ }
+ }
+
+ /**
+ * Convert Duration to string format. The string format will contains 3, 6,
+ * or 9 fractional digits depending on the precision required to represent
+ * the exact Duration value. For example: "1s", "1.010s", "1.000000100s",
+ * "-3.100s" The range that can be represented by Duration is from
+ * -315,576,000,000 to +315,576,000,000 inclusive (in seconds).
+ *
+ * @return The string representation of the given duration.
+ * @throws IllegalArgumentException if the given duration is not in the valid
+ * range.
+ */
+ public static String toString(Duration duration) {
+ long seconds = duration.getSeconds();
+ int nanos = duration.getNanos();
+ checkValid(seconds, nanos);
+
+ StringBuilder result = new StringBuilder();
+ if (seconds < 0 || nanos < 0) {
+ result.append("-");
+ seconds = -seconds;
+ nanos = -nanos;
+ }
+ result.append(seconds);
+ if (nanos != 0) {
+ result.append(".");
+ result.append(Timestamps.formatNanos(nanos));
+ }
+ result.append("s");
+ return result.toString();
+ }
+
+ /**
+ * Parse from a string to produce a duration.
+ *
+ * @return A Duration parsed from the string.
+ * @throws ParseException if parsing fails.
+ */
+ public static Duration parse(String value) throws ParseException {
+ // Must ended with "s".
+ if (value.isEmpty() || value.charAt(value.length() - 1) != 's') {
+ throw new ParseException("Invalid duration string: " + value, 0);
+ }
+ boolean negative = false;
+ if (value.charAt(0) == '-') {
+ negative = true;
+ value = value.substring(1);
+ }
+ String secondValue = value.substring(0, value.length() - 1);
+ String nanoValue = "";
+ int pointPosition = secondValue.indexOf('.');
+ if (pointPosition != -1) {
+ nanoValue = secondValue.substring(pointPosition + 1);
+ secondValue = secondValue.substring(0, pointPosition);
+ }
+ long seconds = Long.parseLong(secondValue);
+ int nanos = nanoValue.isEmpty() ? 0 : Timestamps.parseNanos(nanoValue);
+ if (seconds < 0) {
+ throw new ParseException("Invalid duration string: " + value, 0);
+ }
+ if (negative) {
+ seconds = -seconds;
+ nanos = -nanos;
+ }
+ try {
+ return normalizedDuration(seconds, nanos);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException("Duration value is out of range.", 0);
+ }
+ }
+
+ /**
+ * Create a Duration from the number of milliseconds.
+ */
+ public static Duration fromMillis(long milliseconds) {
+ return normalizedDuration(
+ milliseconds / MILLIS_PER_SECOND,
+ (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
+ }
+
+ /**
+ * Convert a Duration to the number of milliseconds.The result will be
+ * rounded towards 0 to the nearest millisecond. E.g., if the duration
+ * represents -1 nanosecond, it will be rounded to 0.
+ */
+ public static long toMillis(Duration duration) {
+ return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos() / NANOS_PER_MILLISECOND;
+ }
+
+ /**
+ * Create a Duration from the number of microseconds.
+ */
+ public static Duration fromMicros(long microseconds) {
+ return normalizedDuration(
+ microseconds / MICROS_PER_SECOND,
+ (int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
+ }
+
+ /**
+ * Convert a Duration to the number of microseconds.The result will be
+ * rounded towards 0 to the nearest microseconds. E.g., if the duration
+ * represents -1 nanosecond, it will be rounded to 0.
+ */
+ public static long toMicros(Duration duration) {
+ return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos() / NANOS_PER_MICROSECOND;
+ }
+
+ /**
+ * Create a Duration from the number of nanoseconds.
+ */
+ public static Duration fromNanos(long nanoseconds) {
+ return normalizedDuration(
+ nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND));
+ }
+
+ /**
+ * Convert a Duration to the number of nanoseconds.
+ */
+ public static long toNanos(Duration duration) {
+ return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos();
+ }
+
+ /**
+ * Add two durations.
+ */
+ public static Duration add(Duration d1, Duration d2) {
+ return normalizedDuration(d1.getSeconds() + d2.getSeconds(), d1.getNanos() + d2.getNanos());
+ }
+
+ /**
+ * Subtract a duration from another.
+ */
+ public static Duration subtract(Duration d1, Duration d2) {
+ return normalizedDuration(d1.getSeconds() - d2.getSeconds(), d1.getNanos() - d2.getNanos());
+ }
+
+ static Duration normalizedDuration(long seconds, int nanos) {
+ if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
+ seconds += nanos / NANOS_PER_SECOND;
+ nanos %= NANOS_PER_SECOND;
+ }
+ if (seconds > 0 && nanos < 0) {
+ nanos += NANOS_PER_SECOND;
+ seconds -= 1;
+ }
+ if (seconds < 0 && nanos > 0) {
+ nanos -= NANOS_PER_SECOND;
+ seconds += 1;
+ }
+ checkValid(seconds, nanos);
+ return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build();
+ }
+}
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 668d65ab..b577495d 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
@@ -38,6 +38,7 @@ import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
+import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Logger;
@@ -59,22 +60,26 @@ import java.util.logging.Logger;
* intersection to two FieldMasks and traverse all fields specified by the
* FieldMask in a message tree.
*/
-class FieldMaskTree {
+final class FieldMaskTree {
private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
- private static class Node {
- public TreeMap<String, Node> children = new TreeMap<String, Node>();
+ private static final class Node {
+ final SortedMap<String, Node> children = new TreeMap<String, Node>();
}
private final Node root = new Node();
- /** Creates an empty FieldMaskTree. */
- public FieldMaskTree() {}
+ /**
+ * Creates an empty FieldMaskTree.
+ */
+ FieldMaskTree() {}
- /** Creates a FieldMaskTree for a given FieldMask. */
- public FieldMaskTree(FieldMask mask) {
+ /**
+ * Creates a FieldMaskTree for a given FieldMask.
+ */
+ FieldMaskTree(FieldMask mask) {
mergeFromFieldMask(mask);
}
@@ -93,7 +98,7 @@ class FieldMaskTree {
* Likewise, if the field path to add is a sub-path of an existing leaf node,
* nothing will be changed in the tree.
*/
- public FieldMaskTree addFieldPath(String path) {
+ FieldMaskTree addFieldPath(String path) {
String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
if (parts.length == 0) {
return this;
@@ -124,15 +129,17 @@ class FieldMaskTree {
/**
* Merges all field paths in a FieldMask into this tree.
*/
- public FieldMaskTree mergeFromFieldMask(FieldMask mask) {
+ FieldMaskTree mergeFromFieldMask(FieldMask mask) {
for (String path : mask.getPathsList()) {
addFieldPath(path);
}
return this;
}
- /** Converts this tree to a FieldMask. */
- public FieldMask toFieldMask() {
+ /**
+ * Converts this tree to a FieldMask.
+ */
+ FieldMask toFieldMask() {
if (root.children.isEmpty()) {
return FieldMask.getDefaultInstance();
}
@@ -141,7 +148,9 @@ class FieldMaskTree {
return FieldMask.newBuilder().addAllPaths(paths).build();
}
- /** Gathers all field paths in a sub-tree. */
+ /**
+ * Gathers all field paths in a sub-tree.
+ */
private void getFieldPaths(Node node, String path, List<String> paths) {
if (node.children.isEmpty()) {
paths.add(path);
@@ -154,10 +163,9 @@ class FieldMaskTree {
}
/**
- * Adds the intersection of this tree with the given {@code path} to
- * {@code output}.
+ * Adds the intersection of this tree with the given {@code path} to {@code output}.
*/
- public void intersectFieldPath(String path, FieldMaskTree output) {
+ void intersectFieldPath(String path, FieldMaskTree output) {
if (root.children.isEmpty()) {
return;
}
@@ -188,11 +196,9 @@ class FieldMaskTree {
}
/**
- * Merges all fields specified by this FieldMaskTree from {@code source} to
- * {@code destination}.
+ * Merges all fields specified by this FieldMaskTree from {@code source} to {@code destination}.
*/
- public void merge(
- Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
+ void merge(Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
throw new IllegalArgumentException("Cannot merge messages of different types.");
}
@@ -202,8 +208,8 @@ class FieldMaskTree {
merge(root, "", source, destination, options);
}
- /** Merges all fields specified by a sub-tree from {@code source} to
- * {@code destination}.
+ /**
+ * Merges all fields specified by a sub-tree from {@code source} to {@code destination}.
*/
private void merge(
Node node,
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 96961521..21d11b2c 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
@@ -32,6 +32,9 @@ package com.google.protobuf.util;
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
import com.google.common.primitives.Ints;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
@@ -39,7 +42,9 @@ import com.google.protobuf.FieldMask;
import com.google.protobuf.Internal;
import com.google.protobuf.Message;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
@@ -48,7 +53,7 @@ public class FieldMaskUtil {
private static final String FIELD_PATH_SEPARATOR = ",";
private static final String FIELD_PATH_SEPARATOR_REGEX = ",";
private static final String FIELD_SEPARATOR_REGEX = "\\.";
-
+
private FieldMaskUtil() {}
/**
@@ -78,19 +83,17 @@ public class FieldMaskUtil {
*/
public static FieldMask fromString(String value) {
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
- return fromStringList(
- null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
+ return fromStringList(null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
}
/**
* Parses from a string to a FieldMask and validates all field paths.
- *
+ *
* @throws IllegalArgumentException if any of the field path is invalid.
*/
public static FieldMask fromString(Class<? extends Message> type, String value) {
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
- return fromStringList(
- type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
+ return fromStringList(type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
}
/**
@@ -99,8 +102,7 @@ public class FieldMaskUtil {
* @throws IllegalArgumentException if any of the field path is not valid.
*/
// TODO(xiaofeng): Consider renaming fromStrings()
- public static FieldMask fromStringList(
- Class<? extends Message> type, Iterable<String> paths) {
+ public static FieldMask fromStringList(Class<? extends Message> type, Iterable<String> paths) {
FieldMask.Builder builder = FieldMask.newBuilder();
for (String path : paths) {
if (path.isEmpty()) {
@@ -108,8 +110,7 @@ public class FieldMaskUtil {
continue;
}
if (type != null && !isValid(type, path)) {
- throw new IllegalArgumentException(
- path + " is not a valid path for " + type);
+ throw new IllegalArgumentException(path + " is not a valid path for " + type);
}
builder.addPaths(path);
}
@@ -146,15 +147,45 @@ public class FieldMaskUtil {
}
/**
+ * Converts a field mask to a Proto3 JSON string, that is converting from snake case to camel
+ * case and joining all paths into one string with commas.
+ */
+ public static String toJsonString(FieldMask fieldMask) {
+ List<String> paths = new ArrayList<String>(fieldMask.getPathsCount());
+ for (String path : fieldMask.getPathsList()) {
+ if (path.isEmpty()) {
+ continue;
+ }
+ paths.add(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, path));
+ }
+ return Joiner.on(FIELD_PATH_SEPARATOR).join(paths);
+ }
+
+ /**
+ * Converts a field mask from a Proto3 JSON string, that is splitting the paths along commas and
+ * converting from camel case to snake case.
+ */
+ public static FieldMask fromJsonString(String value) {
+ Iterable<String> paths = Splitter.on(FIELD_PATH_SEPARATOR).split(value);
+ FieldMask.Builder builder = FieldMask.newBuilder();
+ for (String path : paths) {
+ if (path.isEmpty()) {
+ continue;
+ }
+ builder.addPaths(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, path));
+ }
+ return builder.build();
+ }
+
+ /**
* Checks whether paths in a given fields mask are valid.
*/
public static boolean isValid(Class<? extends Message> type, FieldMask fieldMask) {
- Descriptor descriptor =
- Internal.getDefaultInstance(type).getDescriptorForType();
-
+ Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
+
return isValid(descriptor, fieldMask);
}
-
+
/**
* Checks whether paths in a given fields mask are valid.
*/
@@ -171,9 +202,8 @@ public class FieldMaskUtil {
* Checks whether a given field path is valid.
*/
public static boolean isValid(Class<? extends Message> type, String path) {
- Descriptor descriptor =
- Internal.getDefaultInstance(type).getDescriptorForType();
-
+ Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
+
return isValid(descriptor, path);
}
@@ -193,8 +223,7 @@ public class FieldMaskUtil {
if (field == null) {
return false;
}
- if (!field.isRepeated()
- && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (!field.isRepeated() && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
descriptor = field.getMessageType();
} else {
descriptor = null;
@@ -202,7 +231,7 @@ public class FieldMaskUtil {
}
return true;
}
-
+
/**
* Converts a FieldMask to its canonical form. In the canonical form of a
* FieldMask, all field paths are sorted alphabetically and redundant field
@@ -251,7 +280,7 @@ public class FieldMaskUtil {
* destination message fields) when merging.
* Default behavior is to merge the source message field into the
* destination message field.
- */
+ */
public boolean replaceMessageFields() {
return replaceMessageFields;
}
@@ -299,16 +328,15 @@ public class FieldMaskUtil {
* Merges fields specified by a FieldMask from one message to another with the
* specified merge options.
*/
- public static void merge(FieldMask mask, Message source,
- Message.Builder destination, MergeOptions options) {
+ public static void merge(
+ FieldMask mask, Message source, Message.Builder destination, MergeOptions options) {
new FieldMaskTree(mask).merge(source, destination, options);
}
/**
* Merges fields specified by a FieldMask from one message to another.
*/
- public static void merge(FieldMask mask, Message source,
- Message.Builder destination) {
+ public static void merge(FieldMask mask, Message source, Message.Builder destination) {
merge(mask, source, destination, new MergeOptions());
}
}
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 76f3437a..bf70834a 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
@@ -60,6 +60,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;
+import com.google.protobuf.NullValue;
import com.google.protobuf.StringValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Timestamp;
@@ -92,18 +93,17 @@ import java.util.logging.Logger;
* as well.
*/
public class JsonFormat {
- private static final Logger logger =
- Logger.getLogger(JsonFormat.class.getName());
+ private static final Logger logger = Logger.getLogger(JsonFormat.class.getName());
private JsonFormat() {}
-
+
/**
* Creates a {@link Printer} with default configurations.
*/
public static Printer printer() {
return new Printer(TypeRegistry.getEmptyTypeRegistry(), false, false);
}
-
+
/**
* A Printer converts protobuf message to JSON format.
*/
@@ -120,11 +120,11 @@ public class JsonFormat {
this.includingDefaultValueFields = includingDefaultValueFields;
this.preservingProtoFieldNames = preservingProtoFieldNames;
}
-
+
/**
* Creates a new {@link Printer} using the given registry. The new Printer
* clones all other configurations from the current {@link Printer}.
- *
+ *
* @throws IllegalArgumentException if a registry is already set.
*/
public Printer usingTypeRegistry(TypeRegistry registry) {
@@ -153,16 +153,15 @@ public class JsonFormat {
public Printer preservingProtoFieldNames() {
return new Printer(registry, includingDefaultValueFields, true);
}
-
+
/**
* Converts a protobuf message to JSON format.
- *
+ *
* @throws InvalidProtocolBufferException if the message contains Any types
* that can't be resolved.
* @throws IOException if writing to the output fails.
*/
- public void appendTo(MessageOrBuilder message, Appendable output)
- throws IOException {
+ public void appendTo(MessageOrBuilder message, Appendable output) throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
new PrinterImpl(registry, includingDefaultValueFields, preservingProtoFieldNames, output)
@@ -171,10 +170,9 @@ public class JsonFormat {
/**
* Converts a protobuf message to JSON format. Throws exceptions if there
- * are unknown Any types in the message.
+ * are unknown Any types in the message.
*/
- public String print(MessageOrBuilder message)
- throws InvalidProtocolBufferException {
+ public String print(MessageOrBuilder message) throws InvalidProtocolBufferException {
try {
StringBuilder builder = new StringBuilder();
appendTo(message, builder);
@@ -194,21 +192,21 @@ public class JsonFormat {
public static Parser parser() {
return new Parser(TypeRegistry.getEmptyTypeRegistry());
}
-
+
/**
* A Parser parses JSON to protobuf message.
*/
public static class Parser {
private final TypeRegistry registry;
-
+
private Parser(TypeRegistry registry) {
- this.registry = registry;
+ this.registry = registry;
}
-
+
/**
* Creates a new {@link Parser} using the given registry. The new Parser
* clones all other configurations from this Parser.
- *
+ *
* @throws IllegalArgumentException if a registry is already set.
*/
public Parser usingTypeRegistry(TypeRegistry registry) {
@@ -217,29 +215,27 @@ public class JsonFormat {
}
return new Parser(registry);
}
-
+
/**
* Parses from JSON into a protobuf message.
- *
+ *
* @throws InvalidProtocolBufferException if the input is not valid JSON
* format or there are unknown fields in the input.
*/
- public void merge(String json, Message.Builder builder)
- throws InvalidProtocolBufferException {
+ public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
new ParserImpl(registry).merge(json, builder);
}
-
+
/**
* Parses from JSON into a protobuf message.
- *
+ *
* @throws InvalidProtocolBufferException if the input is not valid JSON
* format or there are unknown fields in the input.
* @throws IOException if reading from the input throws.
*/
- public void merge(Reader json, Message.Builder builder)
- throws IOException {
+ public void merge(Reader json, Message.Builder builder) throws IOException {
// TODO(xiaofeng): Investigate the allocation overhead and optimize for
// mobile.
new ParserImpl(registry).merge(json, builder);
@@ -255,8 +251,8 @@ public class JsonFormat {
*/
public static class TypeRegistry {
private static class EmptyTypeRegistryHolder {
- private static final TypeRegistry EMPTY = new TypeRegistry(
- Collections.<String, Descriptor>emptyMap());
+ private static final TypeRegistry EMPTY =
+ new TypeRegistry(Collections.<String, Descriptor>emptyMap());
}
public static TypeRegistry getEmptyTypeRegistry() {
@@ -293,8 +289,7 @@ public class JsonFormat {
*/
public Builder add(Descriptor messageType) {
if (types == null) {
- throw new IllegalStateException(
- "A TypeRegistry.Builer can only be used once.");
+ throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
}
addFile(messageType.getFile());
return this;
@@ -306,8 +301,7 @@ public class JsonFormat {
*/
public Builder add(Iterable<Descriptor> messageTypes) {
if (types == null) {
- throw new IllegalStateException(
- "A TypeRegistry.Builer can only be used once.");
+ throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
}
for (Descriptor type : messageTypes) {
addFile(type.getFile());
@@ -345,8 +339,7 @@ public class JsonFormat {
}
if (types.containsKey(message.getFullName())) {
- logger.warning("Type " + message.getFullName()
- + " is added multiple times.");
+ logger.warning("Type " + message.getFullName() + " is added multiple times.");
return;
}
@@ -354,8 +347,7 @@ public class JsonFormat {
}
private final Set<String> files = new HashSet<String>();
- private Map<String, Descriptor> types =
- new HashMap<String, Descriptor>();
+ private Map<String, Descriptor> types = new HashMap<String, Descriptor>();
}
}
@@ -387,8 +379,7 @@ public class JsonFormat {
public void outdent() {
final int length = indent.length();
if (length < 2) {
- throw new IllegalArgumentException(
- " Outdent() without matching Indent().");
+ throw new IllegalArgumentException(" Outdent() without matching Indent().");
}
indent.delete(length - 2, length);
}
@@ -450,45 +441,41 @@ public class JsonFormat {
}
void print(MessageOrBuilder message) throws IOException {
- WellKnownTypePrinter specialPrinter = wellKnownTypePrinters.get(
- message.getDescriptorForType().getFullName());
+ WellKnownTypePrinter specialPrinter =
+ wellKnownTypePrinters.get(message.getDescriptorForType().getFullName());
if (specialPrinter != null) {
specialPrinter.print(this, message);
return;
}
print(message, null);
}
-
+
private interface WellKnownTypePrinter {
- void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException;
- }
-
- private static final Map<String, WellKnownTypePrinter>
- wellKnownTypePrinters = buildWellKnownTypePrinters();
-
- private static Map<String, WellKnownTypePrinter>
- buildWellKnownTypePrinters() {
- Map<String, WellKnownTypePrinter> printers =
- new HashMap<String, WellKnownTypePrinter>();
+ void print(PrinterImpl printer, MessageOrBuilder message) throws IOException;
+ }
+
+ private static final Map<String, WellKnownTypePrinter> wellKnownTypePrinters =
+ buildWellKnownTypePrinters();
+
+ private static Map<String, WellKnownTypePrinter> buildWellKnownTypePrinters() {
+ Map<String, WellKnownTypePrinter> printers = new HashMap<String, WellKnownTypePrinter>();
// Special-case Any.
- printers.put(Any.getDescriptor().getFullName(),
+ printers.put(
+ Any.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printAny(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printAny(message);
+ }
+ });
// Special-case wrapper types.
- WellKnownTypePrinter wrappersPrinter = new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printWrapper(message);
-
- }
- };
+ WellKnownTypePrinter wrappersPrinter =
+ new WellKnownTypePrinter() {
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printWrapper(message);
+ }
+ };
printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
printers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
@@ -499,70 +486,71 @@ public class JsonFormat {
printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
// Special-case Timestamp.
- printers.put(Timestamp.getDescriptor().getFullName(),
+ printers.put(
+ Timestamp.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printTimestamp(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printTimestamp(message);
+ }
+ });
// Special-case Duration.
- printers.put(Duration.getDescriptor().getFullName(),
+ printers.put(
+ Duration.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printDuration(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printDuration(message);
+ }
+ });
// Special-case FieldMask.
- printers.put(FieldMask.getDescriptor().getFullName(),
+ printers.put(
+ FieldMask.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printFieldMask(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printFieldMask(message);
+ }
+ });
// Special-case Struct.
- printers.put(Struct.getDescriptor().getFullName(),
+ printers.put(
+ Struct.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printStruct(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printStruct(message);
+ }
+ });
// Special-case Value.
- printers.put(Value.getDescriptor().getFullName(),
+ printers.put(
+ Value.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printValue(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printValue(message);
+ }
+ });
// Special-case ListValue.
- printers.put(ListValue.getDescriptor().getFullName(),
+ printers.put(
+ ListValue.getDescriptor().getFullName(),
new WellKnownTypePrinter() {
- @Override
- public void print(PrinterImpl printer, MessageOrBuilder message)
- throws IOException {
- printer.printListValue(message);
- }
- });
+ @Override
+ public void print(PrinterImpl printer, MessageOrBuilder message) throws IOException {
+ printer.printListValue(message);
+ }
+ });
return printers;
}
-
+
/** Prints google.protobuf.Any */
private void printAny(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
FieldDescriptor valueField = descriptor.findFieldByName("value");
// Validates type of the message. Note that we can't just cast the message
- // to com.google.protobuf.Any because it might be a DynamicMessage.
- if (typeUrlField == null || valueField == null
+ // to com.google.protobuf.Any because it might be a DynamicMessage.
+ if (typeUrlField == null
+ || valueField == null
|| typeUrlField.getType() != FieldDescriptor.Type.STRING
|| valueField.getType() != FieldDescriptor.Type.BYTES) {
throw new InvalidProtocolBufferException("Invalid Any type.");
@@ -571,12 +559,11 @@ public class JsonFormat {
String typeName = getTypeName(typeUrl);
Descriptor type = registry.find(typeName);
if (type == null) {
- throw new InvalidProtocolBufferException(
- "Cannot find type for url: " + typeUrl);
+ throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
}
ByteString content = (ByteString) message.getField(valueField);
- Message contentMessage = DynamicMessage.getDefaultInstance(type)
- .getParserForType().parseFrom(content);
+ Message contentMessage =
+ DynamicMessage.getDefaultInstance(type).getParserForType().parseFrom(content);
WellKnownTypePrinter printer = wellKnownTypePrinters.get(typeName);
if (printer != null) {
// If the type is one of the well-known types, we use a special
@@ -594,7 +581,7 @@ public class JsonFormat {
print(contentMessage, typeUrl);
}
}
-
+
/** Prints wrapper types (e.g., google.protobuf.Int32Value) */
private void printWrapper(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
@@ -606,7 +593,7 @@ public class JsonFormat {
// the whole message.
printSingleFieldValue(valueField, message.getField(valueField));
}
-
+
private ByteString toByteString(MessageOrBuilder message) {
if (message instanceof Message) {
return ((Message) message).toByteString();
@@ -614,26 +601,25 @@ public class JsonFormat {
return ((Message.Builder) message).build().toByteString();
}
}
-
+
/** Prints google.protobuf.Timestamp */
private void printTimestamp(MessageOrBuilder message) throws IOException {
Timestamp value = Timestamp.parseFrom(toByteString(message));
- generator.print("\"" + TimeUtil.toString(value) + "\"");
+ generator.print("\"" + Timestamps.toString(value) + "\"");
}
-
+
/** Prints google.protobuf.Duration */
private void printDuration(MessageOrBuilder message) throws IOException {
Duration value = Duration.parseFrom(toByteString(message));
- generator.print("\"" + TimeUtil.toString(value) + "\"");
-
+ generator.print("\"" + Durations.toString(value) + "\"");
}
-
+
/** Prints google.protobuf.FieldMask */
private void printFieldMask(MessageOrBuilder message) throws IOException {
FieldMask value = FieldMask.parseFrom(toByteString(message));
- generator.print("\"" + FieldMaskUtil.toString(value) + "\"");
+ generator.print("\"" + FieldMaskUtil.toJsonString(value) + "\"");
}
-
+
/** Prints google.protobuf.Struct */
private void printStruct(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
@@ -644,7 +630,7 @@ public class JsonFormat {
// Struct is formatted as a map object.
printMapFieldValue(field, message.getField(field));
}
-
+
/** Prints google.protobuf.Value */
private void printValue(MessageOrBuilder message) throws IOException {
// For a Value message, only the value of the field is formatted.
@@ -663,7 +649,7 @@ public class JsonFormat {
printSingleFieldValue(entry.getKey(), entry.getValue());
}
}
-
+
/** Prints google.protobuf.ListValue */
private void printListValue(MessageOrBuilder message) throws IOException {
Descriptor descriptor = message.getDescriptorForType();
@@ -675,8 +661,7 @@ public class JsonFormat {
}
/** Prints a regular message with an optional type URL. */
- private void print(MessageOrBuilder message, String typeUrl)
- throws IOException {
+ private void print(MessageOrBuilder message, String typeUrl) throws IOException {
generator.print("{\n");
generator.indent();
@@ -710,7 +695,7 @@ public class JsonFormat {
}
printField(field.getKey(), field.getValue());
}
-
+
// Add line-endings for the last field.
if (printedField) {
generator.print("\n");
@@ -719,8 +704,7 @@ public class JsonFormat {
generator.print("}");
}
- private void printField(FieldDescriptor field, Object value)
- throws IOException {
+ private void printField(FieldDescriptor field, Object value) throws IOException {
if (preservingProtoFieldNames) {
generator.print("\"" + field.getName() + "\": ");
} else {
@@ -734,10 +718,9 @@ public class JsonFormat {
printSingleFieldValue(field, value);
}
}
-
+
@SuppressWarnings("rawtypes")
- private void printRepeatedFieldValue(FieldDescriptor field, Object value)
- throws IOException {
+ private void printRepeatedFieldValue(FieldDescriptor field, Object value) throws IOException {
generator.print("[");
boolean printedElement = false;
for (Object element : (List) value) {
@@ -750,10 +733,9 @@ public class JsonFormat {
}
generator.print("]");
}
-
+
@SuppressWarnings("rawtypes")
- private void printMapFieldValue(FieldDescriptor field, Object value)
- throws IOException {
+ private void printMapFieldValue(FieldDescriptor field, Object value) throws IOException {
Descriptor type = field.getMessageType();
FieldDescriptor keyField = type.findFieldByName("key");
FieldDescriptor valueField = type.findFieldByName("value");
@@ -783,21 +765,20 @@ public class JsonFormat {
generator.outdent();
generator.print("}");
}
-
- private void printSingleFieldValue(FieldDescriptor field, Object value)
- throws IOException {
+
+ private void printSingleFieldValue(FieldDescriptor field, Object value) throws IOException {
printSingleFieldValue(field, value, false);
}
/**
* Prints a field's value in JSON format.
- *
+ *
* @param alwaysWithQuotes whether to always add double-quotes to primitive
* types.
*/
private void printSingleFieldValue(
- final FieldDescriptor field, final Object value,
- boolean alwaysWithQuotes) throws IOException {
+ final FieldDescriptor field, final Object value, boolean alwaysWithQuotes)
+ throws IOException {
switch (field.getType()) {
case INT32:
case SINT32:
@@ -851,7 +832,7 @@ public class JsonFormat {
}
}
break;
-
+
case DOUBLE:
Double doubleValue = (Double) value;
if (doubleValue.isNaN()) {
@@ -895,15 +876,13 @@ public class JsonFormat {
case BYTES:
generator.print("\"");
- generator.print(
- BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
+ generator.print(BaseEncoding.base64().encode(((ByteString) value).toByteArray()));
generator.print("\"");
break;
case ENUM:
// Special-case google.protobuf.NullValue (it's an Enum).
- if (field.getEnumType().getFullName().equals(
- "google.protobuf.NullValue")) {
+ if (field.getEnumType().getFullName().equals("google.protobuf.NullValue")) {
// No matter what value it contains, we always print it as "null".
if (alwaysWithQuotes) {
generator.print("\"");
@@ -914,11 +893,9 @@ public class JsonFormat {
}
} else {
if (((EnumValueDescriptor) value).getIndex() == -1) {
- generator.print(
- String.valueOf(((EnumValueDescriptor) value).getNumber()));
+ generator.print(String.valueOf(((EnumValueDescriptor) value).getNumber()));
} else {
- generator.print(
- "\"" + ((EnumValueDescriptor) value).getName() + "\"");
+ generator.print("\"" + ((EnumValueDescriptor) value).getName() + "\"");
}
}
break;
@@ -947,40 +924,34 @@ public class JsonFormat {
} else {
// Pull off the most-significant bit so that BigInteger doesn't think
// the number is negative, then set it again using setBit().
- return BigInteger.valueOf(value & Long.MAX_VALUE)
- .setBit(Long.SIZE - 1).toString();
+ return BigInteger.valueOf(value & Long.MAX_VALUE).setBit(Long.SIZE - 1).toString();
}
}
-
- private static String getTypeName(String typeUrl)
- throws InvalidProtocolBufferException {
+ private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
String[] parts = typeUrl.split("/");
if (parts.length == 1) {
- throw new InvalidProtocolBufferException(
- "Invalid type url found: " + typeUrl);
+ throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
}
return parts[parts.length - 1];
}
-
+
private static class ParserImpl {
private final TypeRegistry registry;
private final JsonParser jsonParser;
-
+
ParserImpl(TypeRegistry registry) {
this.registry = registry;
this.jsonParser = new JsonParser();
}
-
- void merge(Reader json, Message.Builder builder)
- throws IOException {
+
+ void merge(Reader json, Message.Builder builder) throws IOException {
JsonReader reader = new JsonReader(json);
reader.setLenient(false);
merge(jsonParser.parse(reader), builder);
}
-
- void merge(String json, Message.Builder builder)
- throws InvalidProtocolBufferException {
+
+ void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
try {
JsonReader reader = new JsonReader(new StringReader(json));
reader.setLenient(false);
@@ -992,35 +963,36 @@ public class JsonFormat {
throw new InvalidProtocolBufferException(e.getMessage());
}
}
-
+
private interface WellKnownTypeParser {
void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException;
}
-
+
private static final Map<String, WellKnownTypeParser> wellKnownTypeParsers =
buildWellKnownTypeParsers();
-
- private static Map<String, WellKnownTypeParser>
- buildWellKnownTypeParsers() {
- Map<String, WellKnownTypeParser> parsers =
- new HashMap<String, WellKnownTypeParser>();
+
+ private static Map<String, WellKnownTypeParser> buildWellKnownTypeParsers() {
+ Map<String, WellKnownTypeParser> parsers = new HashMap<String, WellKnownTypeParser>();
// Special-case Any.
- parsers.put(Any.getDescriptor().getFullName(), new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeAny(json, builder);
- }
- });
+ parsers.put(
+ Any.getDescriptor().getFullName(),
+ new WellKnownTypeParser() {
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeAny(json, builder);
+ }
+ });
// Special-case wrapper types.
- WellKnownTypeParser wrappersPrinter = new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeWrapper(json, builder);
- }
- };
+ WellKnownTypeParser wrappersPrinter =
+ new WellKnownTypeParser() {
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeWrapper(json, builder);
+ }
+ };
parsers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter);
@@ -1031,82 +1003,86 @@ public class JsonFormat {
parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter);
parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter);
// Special-case Timestamp.
- parsers.put(Timestamp.getDescriptor().getFullName(),
+ parsers.put(
+ Timestamp.getDescriptor().getFullName(),
new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeTimestamp(json, builder);
- }
- });
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeTimestamp(json, builder);
+ }
+ });
// Special-case Duration.
- parsers.put(Duration.getDescriptor().getFullName(),
+ parsers.put(
+ Duration.getDescriptor().getFullName(),
new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeDuration(json, builder);
- }
- });
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeDuration(json, builder);
+ }
+ });
// Special-case FieldMask.
- parsers.put(FieldMask.getDescriptor().getFullName(),
+ parsers.put(
+ FieldMask.getDescriptor().getFullName(),
new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeFieldMask(json, builder);
- }
- });
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeFieldMask(json, builder);
+ }
+ });
// Special-case Struct.
- parsers.put(Struct.getDescriptor().getFullName(),
+ parsers.put(
+ Struct.getDescriptor().getFullName(),
new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeStruct(json, builder);
- }
- });
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeStruct(json, builder);
+ }
+ });
// Special-case ListValue.
- parsers.put(ListValue.getDescriptor().getFullName(),
+ parsers.put(
+ ListValue.getDescriptor().getFullName(),
new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeListValue(json, builder);
- }
- });
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeListValue(json, builder);
+ }
+ });
// Special-case Value.
- parsers.put(Value.getDescriptor().getFullName(),
+ parsers.put(
+ Value.getDescriptor().getFullName(),
new WellKnownTypeParser() {
- @Override
- public void merge(ParserImpl parser, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
- parser.mergeValue(json, builder);
- }
- });
+ @Override
+ public void merge(ParserImpl parser, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
+ parser.mergeValue(json, builder);
+ }
+ });
return parsers;
}
-
+
private void merge(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
- WellKnownTypeParser specialParser = wellKnownTypeParsers.get(
- builder.getDescriptorForType().getFullName());
+ WellKnownTypeParser specialParser =
+ wellKnownTypeParsers.get(builder.getDescriptorForType().getFullName());
if (specialParser != null) {
specialParser.merge(this, json, builder);
return;
}
mergeMessage(json, builder, false);
}
-
+
// Maps from camel-case field names to FieldDescriptor.
private final Map<Descriptor, Map<String, FieldDescriptor>> fieldNameMaps =
new HashMap<Descriptor, Map<String, FieldDescriptor>>();
-
- private Map<String, FieldDescriptor> getFieldNameMap(
- Descriptor descriptor) {
+
+ private Map<String, FieldDescriptor> getFieldNameMap(Descriptor descriptor) {
if (!fieldNameMaps.containsKey(descriptor)) {
- Map<String, FieldDescriptor> fieldNameMap =
- new HashMap<String, FieldDescriptor>();
+ Map<String, FieldDescriptor> fieldNameMap = new HashMap<String, FieldDescriptor>();
for (FieldDescriptor field : descriptor.getFields()) {
fieldNameMap.put(field.getName(), field);
fieldNameMap.put(field.getJsonName(), field);
@@ -1116,16 +1092,14 @@ public class JsonFormat {
}
return fieldNameMaps.get(descriptor);
}
-
- private void mergeMessage(JsonElement json, Message.Builder builder,
- boolean skipTypeUrl) throws InvalidProtocolBufferException {
+
+ private void mergeMessage(JsonElement json, Message.Builder builder, boolean skipTypeUrl)
+ throws InvalidProtocolBufferException {
if (!(json instanceof JsonObject)) {
- throw new InvalidProtocolBufferException(
- "Expect message object but got: " + json);
+ throw new InvalidProtocolBufferException("Expect message object but got: " + json);
}
JsonObject object = (JsonObject) json;
- Map<String, FieldDescriptor> fieldNameMap =
- getFieldNameMap(builder.getDescriptorForType());
+ Map<String, FieldDescriptor> fieldNameMap = getFieldNameMap(builder.getDescriptorForType());
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
if (skipTypeUrl && entry.getKey().equals("@type")) {
continue;
@@ -1133,47 +1107,46 @@ public class JsonFormat {
FieldDescriptor field = fieldNameMap.get(entry.getKey());
if (field == null) {
throw new InvalidProtocolBufferException(
- "Cannot find field: " + entry.getKey() + " in message "
- + builder.getDescriptorForType().getFullName());
+ "Cannot find field: "
+ + entry.getKey()
+ + " in message "
+ + builder.getDescriptorForType().getFullName());
}
mergeField(field, entry.getValue(), builder);
}
}
-
+
private void mergeAny(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor descriptor = builder.getDescriptorForType();
FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url");
FieldDescriptor valueField = descriptor.findFieldByName("value");
// Validates type of the message. Note that we can't just cast the message
- // to com.google.protobuf.Any because it might be a DynamicMessage.
- if (typeUrlField == null || valueField == null
+ // to com.google.protobuf.Any because it might be a DynamicMessage.
+ if (typeUrlField == null
+ || valueField == null
|| typeUrlField.getType() != FieldDescriptor.Type.STRING
|| valueField.getType() != FieldDescriptor.Type.BYTES) {
throw new InvalidProtocolBufferException("Invalid Any type.");
}
-
+
if (!(json instanceof JsonObject)) {
- throw new InvalidProtocolBufferException(
- "Expect message object but got: " + json);
+ throw new InvalidProtocolBufferException("Expect message object but got: " + json);
}
JsonObject object = (JsonObject) json;
JsonElement typeUrlElement = object.get("@type");
if (typeUrlElement == null) {
- throw new InvalidProtocolBufferException(
- "Missing type url when parsing: " + json);
+ throw new InvalidProtocolBufferException("Missing type url when parsing: " + json);
}
String typeUrl = typeUrlElement.getAsString();
Descriptor contentType = registry.find(getTypeName(typeUrl));
if (contentType == null) {
- throw new InvalidProtocolBufferException(
- "Cannot resolve type: " + typeUrl);
+ throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
}
builder.setField(typeUrlField, typeUrl);
Message.Builder contentBuilder =
DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
- WellKnownTypeParser specialParser =
- wellKnownTypeParsers.get(contentType.getFullName());
+ WellKnownTypeParser specialParser = wellKnownTypeParsers.get(contentType.getFullName());
if (specialParser != null) {
JsonElement value = object.get("value");
if (value != null) {
@@ -1184,35 +1157,33 @@ public class JsonFormat {
}
builder.setField(valueField, contentBuilder.build().toByteString());
}
-
+
private void mergeFieldMask(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
- FieldMask value = FieldMaskUtil.fromString(json.getAsString());
+ FieldMask value = FieldMaskUtil.fromJsonString(json.getAsString());
builder.mergeFrom(value.toByteString());
}
-
+
private void mergeTimestamp(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
try {
- Timestamp value = TimeUtil.parseTimestamp(json.getAsString());
+ Timestamp value = Timestamps.parse(json.getAsString());
builder.mergeFrom(value.toByteString());
} catch (ParseException e) {
- throw new InvalidProtocolBufferException(
- "Failed to parse timestamp: " + json);
+ throw new InvalidProtocolBufferException("Failed to parse timestamp: " + json);
}
}
-
+
private void mergeDuration(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
try {
- Duration value = TimeUtil.parseDuration(json.getAsString());
+ Duration value = Durations.parse(json.getAsString());
builder.mergeFrom(value.toByteString());
} catch (ParseException e) {
- throw new InvalidProtocolBufferException(
- "Failed to parse duration: " + json);
+ throw new InvalidProtocolBufferException("Failed to parse duration: " + json);
}
}
-
+
private void mergeStruct(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor descriptor = builder.getDescriptorForType();
@@ -1232,21 +1203,18 @@ public class JsonFormat {
}
mergeRepeatedField(field, json, builder);
}
-
+
private void mergeValue(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor type = builder.getDescriptorForType();
if (json instanceof JsonPrimitive) {
JsonPrimitive primitive = (JsonPrimitive) json;
if (primitive.isBoolean()) {
- builder.setField(type.findFieldByName("bool_value"),
- primitive.getAsBoolean());
+ builder.setField(type.findFieldByName("bool_value"), primitive.getAsBoolean());
} else if (primitive.isNumber()) {
- builder.setField(type.findFieldByName("number_value"),
- primitive.getAsDouble());
+ builder.setField(type.findFieldByName("number_value"), primitive.getAsDouble());
} else {
- builder.setField(type.findFieldByName("string_value"),
- primitive.getAsString());
+ builder.setField(type.findFieldByName("string_value"), primitive.getAsString());
}
} else if (json instanceof JsonObject) {
FieldDescriptor field = type.findFieldByName("struct_value");
@@ -1262,20 +1230,19 @@ public class JsonFormat {
throw new IllegalStateException("Unexpected json data: " + json);
}
}
-
+
private void mergeWrapper(JsonElement json, Message.Builder builder)
throws InvalidProtocolBufferException {
Descriptor type = builder.getDescriptorForType();
FieldDescriptor field = type.findFieldByName("value");
if (field == null) {
- throw new InvalidProtocolBufferException(
- "Invalid wrapper type: " + type.getFullName());
+ throw new InvalidProtocolBufferException("Invalid wrapper type: " + type.getFullName());
}
builder.setField(field, parseFieldValue(field, json, builder));
}
-
- private void mergeField(FieldDescriptor field, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
+
+ private void mergeField(FieldDescriptor field, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
if (field.isRepeated()) {
if (builder.getRepeatedFieldCount(field) > 0) {
throw new InvalidProtocolBufferException(
@@ -1290,8 +1257,11 @@ public class JsonFormat {
&& builder.getOneofFieldDescriptor(field.getContainingOneof()) != null) {
FieldDescriptor other = builder.getOneofFieldDescriptor(field.getContainingOneof());
throw new InvalidProtocolBufferException(
- "Cannot set field " + field.getFullName() + " because another field "
- + other.getFullName() + " belonging to the same oneof has already been set ");
+ "Cannot set field "
+ + field.getFullName()
+ + " because another field "
+ + other.getFullName()
+ + " belonging to the same oneof has already been set ");
}
}
if (field.isRepeated() && json instanceof JsonNull) {
@@ -1310,44 +1280,38 @@ public class JsonFormat {
}
}
}
-
- private void mergeMapField(FieldDescriptor field, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
+
+ private void mergeMapField(FieldDescriptor field, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
if (!(json instanceof JsonObject)) {
- throw new InvalidProtocolBufferException(
- "Expect a map object but found: " + json);
+ throw new InvalidProtocolBufferException("Expect a map object but found: " + json);
}
Descriptor type = field.getMessageType();
FieldDescriptor keyField = type.findFieldByName("key");
FieldDescriptor valueField = type.findFieldByName("value");
if (keyField == null || valueField == null) {
- throw new InvalidProtocolBufferException(
- "Invalid map field: " + field.getFullName());
+ throw new InvalidProtocolBufferException("Invalid map field: " + field.getFullName());
}
JsonObject object = (JsonObject) json;
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
Message.Builder entryBuilder = builder.newBuilderForField(field);
- Object key = parseFieldValue(
- keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
- Object value = parseFieldValue(
- valueField, entry.getValue(), entryBuilder);
+ Object key = parseFieldValue(keyField, new JsonPrimitive(entry.getKey()), entryBuilder);
+ Object value = parseFieldValue(valueField, entry.getValue(), entryBuilder);
if (value == null) {
- throw new InvalidProtocolBufferException(
- "Map value cannot be null.");
+ throw new InvalidProtocolBufferException("Map value cannot be null.");
}
entryBuilder.setField(keyField, key);
entryBuilder.setField(valueField, value);
builder.addRepeatedField(field, entryBuilder.build());
}
}
-
+
/**
* Gets the default value for a field type. Note that we use proto3
* language defaults and ignore any default values set through the
- * proto "default" option.
+ * proto "default" option.
*/
- private Object getDefaultValue(FieldDescriptor field,
- Message.Builder builder) {
+ private Object getDefaultValue(FieldDescriptor field, Message.Builder builder) {
switch (field.getType()) {
case INT32:
case SINT32:
@@ -1377,30 +1341,27 @@ public class JsonFormat {
case GROUP:
return builder.newBuilderForField(field).getDefaultInstanceForType();
default:
- throw new IllegalStateException(
- "Invalid field type: " + field.getType());
+ throw new IllegalStateException("Invalid field type: " + field.getType());
}
}
-
- private void mergeRepeatedField(FieldDescriptor field, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
+
+ private void mergeRepeatedField(
+ FieldDescriptor field, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
if (!(json instanceof JsonArray)) {
- throw new InvalidProtocolBufferException(
- "Expect an array but found: " + json);
+ throw new InvalidProtocolBufferException("Expect an array but found: " + json);
}
JsonArray array = (JsonArray) json;
for (int i = 0; i < array.size(); ++i) {
Object value = parseFieldValue(field, array.get(i), builder);
if (value == null) {
- throw new InvalidProtocolBufferException(
- "Repeated field elements cannot be null");
+ throw new InvalidProtocolBufferException("Repeated field elements cannot be null");
}
builder.addRepeatedField(field, value);
}
}
-
- private int parseInt32(JsonElement json)
- throws InvalidProtocolBufferException {
+
+ private int parseInt32(JsonElement json) throws InvalidProtocolBufferException {
try {
return Integer.parseInt(json.getAsString());
} catch (Exception e) {
@@ -1416,9 +1377,8 @@ public class JsonFormat {
throw new InvalidProtocolBufferException("Not an int32 value: " + json);
}
}
-
- private long parseInt64(JsonElement json)
- throws InvalidProtocolBufferException {
+
+ private long parseInt64(JsonElement json) throws InvalidProtocolBufferException {
try {
return Long.parseLong(json.getAsString());
} catch (Exception e) {
@@ -1434,14 +1394,12 @@ public class JsonFormat {
throw new InvalidProtocolBufferException("Not an int32 value: " + json);
}
}
-
- private int parseUint32(JsonElement json)
- throws InvalidProtocolBufferException {
+
+ private int parseUint32(JsonElement json) throws InvalidProtocolBufferException {
try {
long result = Long.parseLong(json.getAsString());
if (result < 0 || result > 0xFFFFFFFFL) {
- throw new InvalidProtocolBufferException(
- "Out of range uint32 value: " + json);
+ throw new InvalidProtocolBufferException("Out of range uint32 value: " + json);
}
return (int) result;
} catch (InvalidProtocolBufferException e) {
@@ -1462,35 +1420,28 @@ public class JsonFormat {
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
- throw new InvalidProtocolBufferException(
- "Not an uint32 value: " + json);
+ throw new InvalidProtocolBufferException("Not an uint32 value: " + json);
}
}
-
- private static final BigInteger MAX_UINT64 =
- new BigInteger("FFFFFFFFFFFFFFFF", 16);
-
- private long parseUint64(JsonElement json)
- throws InvalidProtocolBufferException {
+
+ private static final BigInteger MAX_UINT64 = new BigInteger("FFFFFFFFFFFFFFFF", 16);
+
+ private long parseUint64(JsonElement json) throws InvalidProtocolBufferException {
try {
BigDecimal decimalValue = new BigDecimal(json.getAsString());
BigInteger value = decimalValue.toBigIntegerExact();
- if (value.compareTo(BigInteger.ZERO) < 0
- || value.compareTo(MAX_UINT64) > 0) {
- throw new InvalidProtocolBufferException(
- "Out of range uint64 value: " + json);
+ if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(MAX_UINT64) > 0) {
+ throw new InvalidProtocolBufferException("Out of range uint64 value: " + json);
}
return value.longValue();
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
- throw new InvalidProtocolBufferException(
- "Not an uint64 value: " + json);
+ throw new InvalidProtocolBufferException("Not an uint64 value: " + json);
}
}
-
- private boolean parseBool(JsonElement json)
- throws InvalidProtocolBufferException {
+
+ private boolean parseBool(JsonElement json) throws InvalidProtocolBufferException {
if (json.getAsString().equals("true")) {
return true;
}
@@ -1499,11 +1450,10 @@ public class JsonFormat {
}
throw new InvalidProtocolBufferException("Invalid bool value: " + json);
}
-
+
private static final double EPSILON = 1e-6;
-
- private float parseFloat(JsonElement json)
- throws InvalidProtocolBufferException {
+
+ private float parseFloat(JsonElement json) throws InvalidProtocolBufferException {
if (json.getAsString().equals("NaN")) {
return Float.NaN;
} else if (json.getAsString().equals("Infinity")) {
@@ -1521,8 +1471,7 @@ public class JsonFormat {
// of tolerance when checking whether the float value is in range.
if (value > Float.MAX_VALUE * (1.0 + EPSILON)
|| value < -Float.MAX_VALUE * (1.0 + EPSILON)) {
- throw new InvalidProtocolBufferException(
- "Out of range float value: " + json);
+ throw new InvalidProtocolBufferException("Out of range float value: " + json);
}
return (float) value;
} catch (InvalidProtocolBufferException e) {
@@ -1531,19 +1480,17 @@ public class JsonFormat {
throw new InvalidProtocolBufferException("Not a float value: " + json);
}
}
-
- private static final BigDecimal MORE_THAN_ONE = new BigDecimal(
- String.valueOf(1.0 + EPSILON));
+
+ private static final BigDecimal MORE_THAN_ONE = new BigDecimal(String.valueOf(1.0 + EPSILON));
// When a float value is printed, the printed value might be a little
// larger or smaller due to precision loss. Here we need to add a bit
// of tolerance when checking whether the float value is in range.
- private static final BigDecimal MAX_DOUBLE = new BigDecimal(
- String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
- private static final BigDecimal MIN_DOUBLE = new BigDecimal(
- String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
-
- private double parseDouble(JsonElement json)
- throws InvalidProtocolBufferException {
+ private static final BigDecimal MAX_DOUBLE =
+ new BigDecimal(String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
+ private static final BigDecimal MIN_DOUBLE =
+ new BigDecimal(String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE);
+
+ private double parseDouble(JsonElement json) throws InvalidProtocolBufferException {
if (json.getAsString().equals("NaN")) {
return Double.NaN;
} else if (json.getAsString().equals("Infinity")) {
@@ -1556,36 +1503,32 @@ public class JsonFormat {
// accepts all values. Here we parse the value into a BigDecimal and do
// explicit range check on it.
BigDecimal value = new BigDecimal(json.getAsString());
- if (value.compareTo(MAX_DOUBLE) > 0
- || value.compareTo(MIN_DOUBLE) < 0) {
- throw new InvalidProtocolBufferException(
- "Out of range double value: " + json);
+ if (value.compareTo(MAX_DOUBLE) > 0 || value.compareTo(MIN_DOUBLE) < 0) {
+ throw new InvalidProtocolBufferException("Out of range double value: " + json);
}
return value.doubleValue();
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (Exception e) {
- throw new InvalidProtocolBufferException(
- "Not an double value: " + json);
+ throw new InvalidProtocolBufferException("Not an double value: " + json);
}
}
-
+
private String parseString(JsonElement json) {
return json.getAsString();
}
-
+
private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException {
String encoded = json.getAsString();
if (encoded.length() % 4 != 0) {
throw new InvalidProtocolBufferException(
"Bytes field is not encoded in standard BASE64 with paddings: " + encoded);
}
- return ByteString.copyFrom(
- BaseEncoding.base64().decode(json.getAsString()));
+ return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString()));
}
-
- private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor,
- JsonElement json) throws InvalidProtocolBufferException {
+
+ private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, JsonElement json)
+ throws InvalidProtocolBufferException {
String value = json.getAsString();
EnumValueDescriptor result = enumDescriptor.findValueByName(value);
if (result == null) {
@@ -1602,27 +1545,28 @@ public class JsonFormat {
// that's not the exception we want the user to see. Since result == null, we will throw
// an exception later.
}
-
+
if (result == null) {
throw new InvalidProtocolBufferException(
- "Invalid enum value: " + value + " for enum type: "
- + enumDescriptor.getFullName());
+ "Invalid enum value: " + value + " for enum type: " + enumDescriptor.getFullName());
}
}
return result;
}
-
- private Object parseFieldValue(FieldDescriptor field, JsonElement json,
- Message.Builder builder) throws InvalidProtocolBufferException {
+
+ private Object parseFieldValue(FieldDescriptor field, JsonElement json, Message.Builder builder)
+ throws InvalidProtocolBufferException {
if (json instanceof JsonNull) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE
- && field.getMessageType().getFullName().equals(
- Value.getDescriptor().getFullName())) {
+ && field.getMessageType().getFullName().equals(Value.getDescriptor().getFullName())) {
// For every other type, "null" means absence, but for the special
// Value message, it means the "null_value" field has been set.
Value value = Value.newBuilder().setNullValueValue(0).build();
- return builder.newBuilderForField(field).mergeFrom(
- value.toByteString()).build();
+ return builder.newBuilderForField(field).mergeFrom(value.toByteString()).build();
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM
+ && field.getEnumType().getFullName().equals(NullValue.getDescriptor().getFullName())) {
+ // If the type of the field is a NullValue, then the value should be explicitly set.
+ return field.getEnumType().findValueByNumber(0);
}
return null;
}
@@ -1642,7 +1586,7 @@ public class JsonFormat {
case FLOAT:
return parseFloat(json);
-
+
case DOUBLE:
return parseDouble(json);
@@ -1668,11 +1612,10 @@ public class JsonFormat {
Message.Builder subBuilder = builder.newBuilderForField(field);
merge(json, subBuilder);
return subBuilder.build();
-
+
default:
- throw new InvalidProtocolBufferException(
- "Invalid field type: " + field.getType());
- }
+ throw new InvalidProtocolBufferException("Invalid field type: " + field.getType());
+ }
}
}
}
diff --git a/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java b/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
index 3033182a..04758473 100644
--- a/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
+++ b/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java
@@ -35,15 +35,14 @@ import com.google.protobuf.Timestamp;
import java.math.BigInteger;
import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
/**
* Utilities to help create/manipulate Timestamp/Duration
+ *
+ * @deprecated Use {@link Durations} and {@link Timestamps} instead.
*/
-public class TimeUtil {
+@Deprecated
+public final class TimeUtil {
// Timestamp for "0001-01-01T00:00:00Z"
public static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
@@ -53,28 +52,6 @@ public class TimeUtil {
public static final long DURATION_SECONDS_MAX = 315576000000L;
private static final long NANOS_PER_SECOND = 1000000000;
- private static final long NANOS_PER_MILLISECOND = 1000000;
- private static final long NANOS_PER_MICROSECOND = 1000;
- private static final long MILLIS_PER_SECOND = 1000;
- private static final long MICROS_PER_SECOND = 1000000;
-
- private static final ThreadLocal<SimpleDateFormat> timestampFormat =
- new ThreadLocal<SimpleDateFormat>() {
- protected SimpleDateFormat initialValue() {
- return createTimestampFormat();
- }
- };
-
- private static SimpleDateFormat createTimestampFormat() {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- GregorianCalendar calendar =
- new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- // We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends
- // backwards to year one) for timestamp formating.
- calendar.setGregorianChange(new Date(Long.MIN_VALUE));
- sdf.setCalendar(calendar);
- return sdf;
- }
private TimeUtil() {}
@@ -90,27 +67,11 @@ public class TimeUtil {
* @return The string representation of the given timestamp.
* @throws IllegalArgumentException if the given timestamp is not in the
* valid range.
+ * @deprecated Use {@link Timestamps#toString} instead.
*/
- public static String toString(Timestamp timestamp)
- throws IllegalArgumentException {
- StringBuilder result = new StringBuilder();
- // Format the seconds part.
- if (timestamp.getSeconds() < TIMESTAMP_SECONDS_MIN
- || timestamp.getSeconds() > TIMESTAMP_SECONDS_MAX) {
- throw new IllegalArgumentException("Timestamp is out of range.");
- }
- Date date = new Date(timestamp.getSeconds() * MILLIS_PER_SECOND);
- result.append(timestampFormat.get().format(date));
- // Format the nanos part.
- if (timestamp.getNanos() < 0 || timestamp.getNanos() >= NANOS_PER_SECOND) {
- throw new IllegalArgumentException("Timestamp has invalid nanos value.");
- }
- if (timestamp.getNanos() != 0) {
- result.append(".");
- result.append(formatNanos(timestamp.getNanos()));
- }
- result.append("Z");
- return result.toString();
+ @Deprecated
+ public static String toString(Timestamp timestamp) {
+ return Timestamps.toString(timestamp);
}
/**
@@ -123,59 +84,11 @@ public class TimeUtil {
*
* @return A Timestamp parsed from the string.
* @throws ParseException if parsing fails.
+ * @deprecated Use {@link Timestamps#parse} instead.
*/
-
+ @Deprecated
public static Timestamp parseTimestamp(String value) throws ParseException {
- int dayOffset = value.indexOf('T');
- if (dayOffset == -1) {
- throw new ParseException(
- "Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0);
- }
- int timezoneOffsetPosition = value.indexOf('Z', dayOffset);
- if (timezoneOffsetPosition == -1) {
- timezoneOffsetPosition = value.indexOf('+', dayOffset);
- }
- if (timezoneOffsetPosition == -1) {
- timezoneOffsetPosition = value.indexOf('-', dayOffset);
- }
- if (timezoneOffsetPosition == -1) {
- throw new ParseException(
- "Failed to parse timestamp: missing valid timezone offset.", 0);
- }
- // Parse seconds and nanos.
- String timeValue = value.substring(0, timezoneOffsetPosition);
- String secondValue = timeValue;
- String nanoValue = "";
- int pointPosition = timeValue.indexOf('.');
- if (pointPosition != -1) {
- secondValue = timeValue.substring(0, pointPosition);
- nanoValue = timeValue.substring(pointPosition + 1);
- }
- Date date = timestampFormat.get().parse(secondValue);
- long seconds = date.getTime() / MILLIS_PER_SECOND;
- int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
- // Parse timezone offsets.
- if (value.charAt(timezoneOffsetPosition) == 'Z') {
- if (value.length() != timezoneOffsetPosition + 1) {
- throw new ParseException(
- "Failed to parse timestamp: invalid trailing data \""
- + value.substring(timezoneOffsetPosition) + "\"", 0);
- }
- } else {
- String offsetValue = value.substring(timezoneOffsetPosition + 1);
- long offset = parseTimezoneOffset(offsetValue);
- if (value.charAt(timezoneOffsetPosition) == '+') {
- seconds -= offset;
- } else {
- seconds += offset;
- }
- }
- try {
- return normalizedTimestamp(seconds, nanos);
- } catch (IllegalArgumentException e) {
- throw new ParseException(
- "Failed to parse timestmap: timestamp is out of range.", 0);
- }
+ return Timestamps.parse(value);
}
/**
@@ -188,33 +101,11 @@ public class TimeUtil {
* @return The string representation of the given duration.
* @throws IllegalArgumentException if the given duration is not in the valid
* range.
+ * @deprecated Use {@link Durations#toString} instead.
*/
- public static String toString(Duration duration)
- throws IllegalArgumentException {
- if (duration.getSeconds() < DURATION_SECONDS_MIN
- || duration.getSeconds() > DURATION_SECONDS_MAX) {
- throw new IllegalArgumentException("Duration is out of valid range.");
- }
- StringBuilder result = new StringBuilder();
- long seconds = duration.getSeconds();
- int nanos = duration.getNanos();
- if (seconds < 0 || nanos < 0) {
- if (seconds > 0 || nanos > 0) {
- throw new IllegalArgumentException(
- "Invalid duration: seconds value and nanos value must have the same"
- + "sign.");
- }
- result.append("-");
- seconds = -seconds;
- nanos = -nanos;
- }
- result.append(seconds);
- if (nanos != 0) {
- result.append(".");
- result.append(formatNanos(nanos));
- }
- result.append("s");
- return result.toString();
+ @Deprecated
+ public static String toString(Duration duration) {
+ return Durations.toString(duration);
}
/**
@@ -222,54 +113,31 @@ public class TimeUtil {
*
* @return A Duration parsed from the string.
* @throws ParseException if parsing fails.
+ * @deprecated Use {@link Durations#parse} instead.
*/
+ @Deprecated
public static Duration parseDuration(String value) throws ParseException {
- // Must ended with "s".
- if (value.isEmpty() || value.charAt(value.length() - 1) != 's') {
- throw new ParseException("Invalid duration string: " + value, 0);
- }
- boolean negative = false;
- if (value.charAt(0) == '-') {
- negative = true;
- value = value.substring(1);
- }
- String secondValue = value.substring(0, value.length() - 1);
- String nanoValue = "";
- int pointPosition = secondValue.indexOf('.');
- if (pointPosition != -1) {
- nanoValue = secondValue.substring(pointPosition + 1);
- secondValue = secondValue.substring(0, pointPosition);
- }
- long seconds = Long.parseLong(secondValue);
- int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
- if (seconds < 0) {
- throw new ParseException("Invalid duration string: " + value, 0);
- }
- if (negative) {
- seconds = -seconds;
- nanos = -nanos;
- }
- try {
- return normalizedDuration(seconds, nanos);
- } catch (IllegalArgumentException e) {
- throw new ParseException("Duration value is out of range.", 0);
- }
+ return Durations.parse(value);
}
/**
* Create a Timestamp from the number of milliseconds elapsed from the epoch.
+ *
+ * @deprecated Use {@link Timestamps#fromMillis} instead.
*/
+ @Deprecated
public static Timestamp createTimestampFromMillis(long milliseconds) {
- return normalizedTimestamp(milliseconds / MILLIS_PER_SECOND,
- (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
+ return Timestamps.fromMillis(milliseconds);
}
/**
* Create a Duration from the number of milliseconds.
+ *
+ * @deprecated Use {@link Durations#fromMillis} instead.
*/
+ @Deprecated
public static Duration createDurationFromMillis(long milliseconds) {
- return normalizedDuration(milliseconds / MILLIS_PER_SECOND,
- (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
+ return Durations.fromMillis(milliseconds);
}
/**
@@ -278,36 +146,44 @@ public class TimeUtil {
* <p>The result will be rounded down to the nearest millisecond. E.g., if the
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
* to -1 millisecond.
+ *
+ * @deprecated Use {@link Timestamps#toMillis} instead.
*/
+ @Deprecated
public static long toMillis(Timestamp timestamp) {
- return timestamp.getSeconds() * MILLIS_PER_SECOND + timestamp.getNanos()
- / NANOS_PER_MILLISECOND;
+ return Timestamps.toMillis(timestamp);
}
/**
* Convert a Duration to the number of milliseconds.The result will be
* rounded towards 0 to the nearest millisecond. E.g., if the duration
* represents -1 nanosecond, it will be rounded to 0.
+ *
+ * @deprecated Use {@link Durations#toMillis} instead.
*/
+ @Deprecated
public static long toMillis(Duration duration) {
- return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos()
- / NANOS_PER_MILLISECOND;
+ return Durations.toMillis(duration);
}
/**
* Create a Timestamp from the number of microseconds elapsed from the epoch.
+ *
+ * @deprecated Use {@link Timestamps#fromMicros} instead.
*/
+ @Deprecated
public static Timestamp createTimestampFromMicros(long microseconds) {
- return normalizedTimestamp(microseconds / MICROS_PER_SECOND,
- (int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
+ return Timestamps.fromMicros(microseconds);
}
/**
* Create a Duration from the number of microseconds.
+ *
+ * @deprecated Use {@link Durations#fromMicros} instead.
*/
+ @Deprecated
public static Duration createDurationFromMicros(long microseconds) {
- return normalizedDuration(microseconds / MICROS_PER_SECOND,
- (int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
+ return Durations.fromMicros(microseconds);
}
/**
@@ -316,111 +192,141 @@ public class TimeUtil {
* <p>The result will be rounded down to the nearest microsecond. E.g., if the
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
* to -1 millisecond.
+ *
+ * @deprecated Use {@link Timestamps#toMicros} instead.
*/
+ @Deprecated
public static long toMicros(Timestamp timestamp) {
- return timestamp.getSeconds() * MICROS_PER_SECOND + timestamp.getNanos()
- / NANOS_PER_MICROSECOND;
+ return Timestamps.toMicros(timestamp);
}
/**
* Convert a Duration to the number of microseconds.The result will be
* rounded towards 0 to the nearest microseconds. E.g., if the duration
* represents -1 nanosecond, it will be rounded to 0.
+ *
+ * @deprecated Use {@link Durations#toMicros} instead.
*/
+ @Deprecated
public static long toMicros(Duration duration) {
- return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos()
- / NANOS_PER_MICROSECOND;
+ return Durations.toMicros(duration);
}
/**
* Create a Timestamp from the number of nanoseconds elapsed from the epoch.
+ *
+ * @deprecated Use {@link Timestamps#fromNanos} instead.
*/
+ @Deprecated
public static Timestamp createTimestampFromNanos(long nanoseconds) {
- return normalizedTimestamp(nanoseconds / NANOS_PER_SECOND,
- (int) (nanoseconds % NANOS_PER_SECOND));
+ return Timestamps.fromNanos(nanoseconds);
}
/**
* Create a Duration from the number of nanoseconds.
+ *
+ * @deprecated Use {@link Durations#fromNanos} instead.
*/
+ @Deprecated
public static Duration createDurationFromNanos(long nanoseconds) {
- return normalizedDuration(nanoseconds / NANOS_PER_SECOND,
- (int) (nanoseconds % NANOS_PER_SECOND));
+ return Durations.fromNanos(nanoseconds);
}
/**
* Convert a Timestamp to the number of nanoseconds elapsed from the epoch.
+ *
+ * @deprecated Use {@link Timestamps#toNanos} instead.
*/
+ @Deprecated
public static long toNanos(Timestamp timestamp) {
- return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos();
+ return Timestamps.toNanos(timestamp);
}
/**
* Convert a Duration to the number of nanoseconds.
+ *
+ * @deprecated Use {@link Durations#toNanos} instead.
*/
+ @Deprecated
public static long toNanos(Duration duration) {
- return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos();
+ return Durations.toNanos(duration);
}
/**
* Get the current time.
+ *
+ * @deprecated Use {@code Timestamps.fromMillis(System.currentTimeMillis())} instead.
*/
+ @Deprecated
public static Timestamp getCurrentTime() {
- return createTimestampFromMillis(System.currentTimeMillis());
+ return Timestamps.fromMillis(System.currentTimeMillis());
}
/**
* Get the epoch.
+ *
+ * @deprecated Use {@code Timestamps.fromMillis(0)} instead.
*/
+ @Deprecated
public static Timestamp getEpoch() {
return Timestamp.getDefaultInstance();
}
/**
* Calculate the difference between two timestamps.
+ *
+ * @deprecated Use {@link Timestamps#between} instead.
*/
+ @Deprecated
public static Duration distance(Timestamp from, Timestamp to) {
- return normalizedDuration(to.getSeconds() - from.getSeconds(),
- to.getNanos() - from.getNanos());
+ return Timestamps.between(from, to);
}
/**
* Add a duration to a timestamp.
+ *
+ * @deprecated Use {@link Timestamps#add} instead.
*/
+ @Deprecated
public static Timestamp add(Timestamp start, Duration length) {
- return normalizedTimestamp(start.getSeconds() + length.getSeconds(),
- start.getNanos() + length.getNanos());
+ return Timestamps.add(start, length);
}
/**
* Subtract a duration from a timestamp.
+ *
+ * @deprecated Use {@link Timestamps#subtract} instead.
*/
+ @Deprecated
public static Timestamp subtract(Timestamp start, Duration length) {
- return normalizedTimestamp(start.getSeconds() - length.getSeconds(),
- start.getNanos() - length.getNanos());
+ return Timestamps.subtract(start, length);
}
/**
* Add two durations.
+ *
+ * @deprecated Use {@link Durations#add} instead.
*/
+ @Deprecated
public static Duration add(Duration d1, Duration d2) {
- return normalizedDuration(d1.getSeconds() + d2.getSeconds(),
- d1.getNanos() + d2.getNanos());
+ return Durations.add(d1, d2);
}
/**
* Subtract a duration from another.
+ *
+ * @deprecated Use {@link Durations#subtract} instead.
*/
+ @Deprecated
public static Duration subtract(Duration d1, Duration d2) {
- return normalizedDuration(d1.getSeconds() - d2.getSeconds(),
- d1.getNanos() - d2.getNanos());
+ return Durations.subtract(d1, d2);
}
// Multiplications and divisions.
+ // TODO(kak): Delete this.
public static Duration multiply(Duration duration, double times) {
- double result = duration.getSeconds() * times + duration.getNanos() * times
- / 1000000000.0;
+ double result = duration.getSeconds() * times + duration.getNanos() * times / 1000000000.0;
if (result < Long.MIN_VALUE || result > Long.MAX_VALUE) {
throw new IllegalArgumentException("Result is out of valid range.");
}
@@ -428,50 +334,49 @@ public class TimeUtil {
int nanos = (int) ((result - seconds) * 1000000000);
return normalizedDuration(seconds, nanos);
}
-
+
+ // TODO(kak): Delete this.
public static Duration divide(Duration duration, double value) {
return multiply(duration, 1.0 / value);
}
-
+
+ // TODO(kak): Delete this.
public static Duration multiply(Duration duration, long times) {
- return createDurationFromBigInteger(
- toBigInteger(duration).multiply(toBigInteger(times)));
+ return createDurationFromBigInteger(toBigInteger(duration).multiply(toBigInteger(times)));
}
-
+
+ // TODO(kak): Delete this.
public static Duration divide(Duration duration, long times) {
- return createDurationFromBigInteger(
- toBigInteger(duration).divide(toBigInteger(times)));
+ return createDurationFromBigInteger(toBigInteger(duration).divide(toBigInteger(times)));
}
-
+
+ // TODO(kak): Delete this.
public static long divide(Duration d1, Duration d2) {
return toBigInteger(d1).divide(toBigInteger(d2)).longValue();
}
-
+
+ // TODO(kak): Delete this.
public static Duration remainder(Duration d1, Duration d2) {
- return createDurationFromBigInteger(
- toBigInteger(d1).remainder(toBigInteger(d2)));
+ return createDurationFromBigInteger(toBigInteger(d1).remainder(toBigInteger(d2)));
}
-
+
private static final BigInteger NANOS_PER_SECOND_BIG_INTEGER =
new BigInteger(String.valueOf(NANOS_PER_SECOND));
-
+
private static BigInteger toBigInteger(Duration duration) {
return toBigInteger(duration.getSeconds())
- .multiply(NANOS_PER_SECOND_BIG_INTEGER)
- .add(toBigInteger(duration.getNanos()));
+ .multiply(NANOS_PER_SECOND_BIG_INTEGER)
+ .add(toBigInteger(duration.getNanos()));
}
-
+
private static BigInteger toBigInteger(long value) {
return new BigInteger(String.valueOf(value));
}
-
+
private static Duration createDurationFromBigInteger(BigInteger value) {
- long seconds = value.divide(
- new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue();
- int nanos = value.remainder(
- new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue();
+ long seconds = value.divide(new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue();
+ int nanos = value.remainder(new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue();
return normalizedDuration(seconds, nanos);
-
}
private static Duration normalizedDuration(long seconds, int nanos) {
@@ -492,58 +397,4 @@ public class TimeUtil {
}
return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build();
}
-
- private static Timestamp normalizedTimestamp(long seconds, int nanos) {
- if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
- seconds += nanos / NANOS_PER_SECOND;
- nanos %= NANOS_PER_SECOND;
- }
- if (nanos < 0) {
- nanos += NANOS_PER_SECOND;
- seconds -= 1;
- }
- if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) {
- throw new IllegalArgumentException("Timestamp is out of valid range.");
- }
- return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
- }
-
- /**
- * Format the nano part of a timestamp or a duration.
- */
- private static String formatNanos(int nanos) {
- assert nanos >= 1 && nanos <= 999999999;
- // Determine whether to use 3, 6, or 9 digits for the nano part.
- if (nanos % NANOS_PER_MILLISECOND == 0) {
- return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND);
- } else if (nanos % NANOS_PER_MICROSECOND == 0) {
- return String.format("%1$06d", nanos / NANOS_PER_MICROSECOND);
- } else {
- return String.format("%1$09d", nanos);
- }
- }
-
- private static int parseNanos(String value) throws ParseException {
- int result = 0;
- for (int i = 0; i < 9; ++i) {
- result = result * 10;
- if (i < value.length()) {
- if (value.charAt(i) < '0' || value.charAt(i) > '9') {
- throw new ParseException("Invalid nanosecnds.", 0);
- }
- result += value.charAt(i) - '0';
- }
- }
- return result;
- }
-
- private static long parseTimezoneOffset(String value) throws ParseException {
- int pos = value.indexOf(':');
- if (pos == -1) {
- throw new ParseException("Invalid offset value: " + value, 0);
- }
- String hours = value.substring(0, pos);
- String minutes = value.substring(pos + 1);
- return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60;
- }
}
diff --git a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
new file mode 100644
index 00000000..f1f202da
--- /dev/null
+++ b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
@@ -0,0 +1,349 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf.util;
+
+import com.google.protobuf.Duration;
+import com.google.protobuf.Timestamp;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+ * Utilities to help create/manipulate {@code protobuf/timestamp.proto}.
+ */
+public final class Timestamps {
+ // Timestamp for "0001-01-01T00:00:00Z"
+ static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
+
+ // Timestamp for "9999-12-31T23:59:59Z"
+ static final long TIMESTAMP_SECONDS_MAX = 253402300799L;
+
+ static final long NANOS_PER_SECOND = 1000000000;
+ static final long NANOS_PER_MILLISECOND = 1000000;
+ static final long NANOS_PER_MICROSECOND = 1000;
+ static final long MILLIS_PER_SECOND = 1000;
+ static final long MICROS_PER_SECOND = 1000000;
+
+ // TODO(kak): Do we want to expose Timestamp constants for MAX/MIN?
+
+ private static final ThreadLocal<SimpleDateFormat> timestampFormat =
+ new ThreadLocal<SimpleDateFormat>() {
+ protected SimpleDateFormat initialValue() {
+ return createTimestampFormat();
+ }
+ };
+
+ private static SimpleDateFormat createTimestampFormat() {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ // We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends
+ // backwards to year one) for timestamp formating.
+ calendar.setGregorianChange(new Date(Long.MIN_VALUE));
+ sdf.setCalendar(calendar);
+ return sdf;
+ }
+
+ private Timestamps() {}
+
+ /**
+ * Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the
+ * range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and
+ * 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range [0, +999,999,999].
+ *
+ * <p>Note: Negative second values with fractions must still have non-negative nanos value that
+ * counts forward in time.
+ */
+ public static boolean isValid(Timestamp timestamp) {
+ return isValid(timestamp.getSeconds(), timestamp.getNanos());
+ }
+
+ /**
+ * Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The
+ * {@code seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between
+ * 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range
+ * [0, +999,999,999].
+ *
+ * <p>Note: Negative second values with fractions must still have non-negative nanos value that
+ * counts forward in time.
+ */
+ public static boolean isValid(long seconds, long nanos) {
+ if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) {
+ return false;
+ }
+ if (nanos < 0 || nanos >= NANOS_PER_SECOND) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Throws an {@link IllegalArgumentException} if the given seconds/nanos are not
+ * a valid {@link Timestamp}.
+ */
+ private static void checkValid(long seconds, int nanos) {
+ if (!isValid(seconds, nanos)) {
+ throw new IllegalArgumentException(String.format(
+ "Timestamp is not valid. See proto definition for valid values. "
+ + "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]."
+ + "Nanos (%s) must be in range [0, +999,999,999].",
+ seconds, nanos));
+ }
+ }
+
+ /**
+ * Convert Timestamp to RFC 3339 date string format. The output will always
+ * be Z-normalized and uses 3, 6 or 9 fractional digits as required to
+ * represent the exact value. Note that Timestamp can only represent time
+ * from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See
+ * https://www.ietf.org/rfc/rfc3339.txt
+ *
+ * <p>Example of generated format: "1972-01-01T10:00:20.021Z"
+ *
+ * @return The string representation of the given timestamp.
+ * @throws IllegalArgumentException if the given timestamp is not in the
+ * valid range.
+ */
+ public static String toString(Timestamp timestamp) {
+ long seconds = timestamp.getSeconds();
+ int nanos = timestamp.getNanos();
+ checkValid(seconds, nanos);
+ StringBuilder result = new StringBuilder();
+ // Format the seconds part.
+ Date date = new Date(seconds * MILLIS_PER_SECOND);
+ result.append(timestampFormat.get().format(date));
+ // Format the nanos part.
+ if (nanos != 0) {
+ result.append(".");
+ result.append(formatNanos(nanos));
+ }
+ result.append("Z");
+ return result.toString();
+ }
+
+ /**
+ * Parse from RFC 3339 date string to Timestamp. This method accepts all
+ * outputs of {@link #toString(Timestamp)} and it also accepts any fractional
+ * digits (or none) and any offset as long as they fit into nano-seconds
+ * precision.
+ *
+ * <p>Example of accepted format: "1972-01-01T10:00:20.021-05:00"
+ *
+ * @return A Timestamp parsed from the string.
+ * @throws ParseException if parsing fails.
+ */
+ public static Timestamp parse(String value) throws ParseException {
+ int dayOffset = value.indexOf('T');
+ if (dayOffset == -1) {
+ throw new ParseException("Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0);
+ }
+ int timezoneOffsetPosition = value.indexOf('Z', dayOffset);
+ if (timezoneOffsetPosition == -1) {
+ timezoneOffsetPosition = value.indexOf('+', dayOffset);
+ }
+ if (timezoneOffsetPosition == -1) {
+ timezoneOffsetPosition = value.indexOf('-', dayOffset);
+ }
+ if (timezoneOffsetPosition == -1) {
+ throw new ParseException("Failed to parse timestamp: missing valid timezone offset.", 0);
+ }
+ // Parse seconds and nanos.
+ String timeValue = value.substring(0, timezoneOffsetPosition);
+ String secondValue = timeValue;
+ String nanoValue = "";
+ int pointPosition = timeValue.indexOf('.');
+ if (pointPosition != -1) {
+ secondValue = timeValue.substring(0, pointPosition);
+ nanoValue = timeValue.substring(pointPosition + 1);
+ }
+ Date date = timestampFormat.get().parse(secondValue);
+ long seconds = date.getTime() / MILLIS_PER_SECOND;
+ int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
+ // Parse timezone offsets.
+ if (value.charAt(timezoneOffsetPosition) == 'Z') {
+ if (value.length() != timezoneOffsetPosition + 1) {
+ throw new ParseException(
+ "Failed to parse timestamp: invalid trailing data \""
+ + value.substring(timezoneOffsetPosition)
+ + "\"",
+ 0);
+ }
+ } else {
+ String offsetValue = value.substring(timezoneOffsetPosition + 1);
+ long offset = parseTimezoneOffset(offsetValue);
+ if (value.charAt(timezoneOffsetPosition) == '+') {
+ seconds -= offset;
+ } else {
+ seconds += offset;
+ }
+ }
+ try {
+ return normalizedTimestamp(seconds, nanos);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException("Failed to parse timestmap: timestamp is out of range.", 0);
+ }
+ }
+
+ /**
+ * Create a Timestamp from the number of milliseconds elapsed from the epoch.
+ */
+ public static Timestamp fromMillis(long milliseconds) {
+ return normalizedTimestamp(
+ milliseconds / MILLIS_PER_SECOND,
+ (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
+ }
+
+ /**
+ * Convert a Timestamp to the number of milliseconds elapsed from the epoch.
+ *
+ * <p>The result will be rounded down to the nearest millisecond. E.g., if the
+ * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
+ * to -1 millisecond.
+ */
+ public static long toMillis(Timestamp timestamp) {
+ return timestamp.getSeconds() * MILLIS_PER_SECOND
+ + timestamp.getNanos() / NANOS_PER_MILLISECOND;
+ }
+
+ /**
+ * Create a Timestamp from the number of microseconds elapsed from the epoch.
+ */
+ public static Timestamp fromMicros(long microseconds) {
+ return normalizedTimestamp(
+ microseconds / MICROS_PER_SECOND,
+ (int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
+ }
+
+ /**
+ * Convert a Timestamp to the number of microseconds elapsed from the epoch.
+ *
+ * <p>The result will be rounded down to the nearest microsecond. E.g., if the
+ * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded
+ * to -1 millisecond.
+ */
+ public static long toMicros(Timestamp timestamp) {
+ return timestamp.getSeconds() * MICROS_PER_SECOND
+ + timestamp.getNanos() / NANOS_PER_MICROSECOND;
+ }
+
+ /**
+ * Create a Timestamp from the number of nanoseconds elapsed from the epoch.
+ */
+ public static Timestamp fromNanos(long nanoseconds) {
+ return normalizedTimestamp(
+ nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND));
+ }
+
+ /**
+ * Convert a Timestamp to the number of nanoseconds elapsed from the epoch.
+ */
+ public static long toNanos(Timestamp timestamp) {
+ return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos();
+ }
+
+ /**
+ * Calculate the difference between two timestamps.
+ */
+ public static Duration between(Timestamp from, Timestamp to) {
+ return Durations.normalizedDuration(
+ to.getSeconds() - from.getSeconds(), to.getNanos() - from.getNanos());
+ }
+
+ /**
+ * Add a duration to a timestamp.
+ */
+ public static Timestamp add(Timestamp start, Duration length) {
+ return normalizedTimestamp(
+ start.getSeconds() + length.getSeconds(), start.getNanos() + length.getNanos());
+ }
+
+ /**
+ * Subtract a duration from a timestamp.
+ */
+ public static Timestamp subtract(Timestamp start, Duration length) {
+ return normalizedTimestamp(
+ start.getSeconds() - length.getSeconds(), start.getNanos() - length.getNanos());
+ }
+
+ private static Timestamp normalizedTimestamp(long seconds, int nanos) {
+ if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
+ seconds += nanos / NANOS_PER_SECOND;
+ nanos %= NANOS_PER_SECOND;
+ }
+ if (nanos < 0) {
+ nanos += NANOS_PER_SECOND;
+ seconds -= 1;
+ }
+ checkValid(seconds, nanos);
+ return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
+ }
+
+ private static long parseTimezoneOffset(String value) throws ParseException {
+ int pos = value.indexOf(':');
+ if (pos == -1) {
+ throw new ParseException("Invalid offset value: " + value, 0);
+ }
+ String hours = value.substring(0, pos);
+ String minutes = value.substring(pos + 1);
+ return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60;
+ }
+
+ static int parseNanos(String value) throws ParseException {
+ int result = 0;
+ for (int i = 0; i < 9; ++i) {
+ result = result * 10;
+ if (i < value.length()) {
+ if (value.charAt(i) < '0' || value.charAt(i) > '9') {
+ throw new ParseException("Invalid nanosecnds.", 0);
+ }
+ result += value.charAt(i) - '0';
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Format the nano part of a timestamp or a duration.
+ */
+ static String formatNanos(int nanos) {
+ assert nanos >= 1 && nanos <= 999999999;
+ // Determine whether to use 3, 6, or 9 digits for the nano part.
+ if (nanos % NANOS_PER_MILLISECOND == 0) {
+ return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND);
+ } else if (nanos % NANOS_PER_MICROSECOND == 0) {
+ return String.format("%1$06d", nanos / NANOS_PER_MICROSECOND);
+ } else {
+ return String.format("%1$09d", nanos);
+ }
+ }
+}
diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
index 194f7b9c..1a998570 100644
--- a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
@@ -41,52 +41,55 @@ public class FieldMaskUtilTest extends TestCase {
public void testIsValid() throws Exception {
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist"));
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.optional_int32"));
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.repeated_int32"));
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.optional_nested_message"));
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.repeated_nested_message"));
- assertFalse(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.nonexist"));
-
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
- assertFalse(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
- assertFalse(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")));
-
+ assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32"));
+ assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_int32"));
+ assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message"));
+ assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message"));
+ assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.nonexist"));
+
+ assertTrue(
+ FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")));
+ assertFalse(
+ FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")));
+ assertFalse(
+ FieldMaskUtil.isValid(
+ NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")));
+
assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload"));
assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist"));
-
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
- assertFalse(FieldMaskUtil.isValid(
- NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
-
- assertTrue(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
+
+ assertTrue(
+ FieldMaskUtil.isValid(
+ NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")));
+ assertFalse(
+ FieldMaskUtil.isValid(
+ NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")));
+
+ assertTrue(
+ FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message.bb"));
// Repeated fields cannot have sub-paths.
- assertFalse(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.repeated_nested_message.bb"));
+ assertFalse(
+ FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message.bb"));
// Non-message fields cannot have sub-paths.
- assertFalse(FieldMaskUtil.isValid(
- NestedTestAllTypes.class, "payload.optional_int32.bb"));
+ assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32.bb"));
}
-
+
public void testToString() throws Exception {
assertEquals("", FieldMaskUtil.toString(FieldMask.getDefaultInstance()));
FieldMask mask = FieldMask.newBuilder().addPaths("foo").build();
assertEquals("foo", FieldMaskUtil.toString(mask));
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar").build();
assertEquals("foo,bar", FieldMaskUtil.toString(mask));
-
+
// Empty field paths are ignored.
- mask = FieldMask.newBuilder().addPaths("").addPaths("foo").addPaths("").
- addPaths("bar").addPaths("").build();
+ mask =
+ FieldMask.newBuilder()
+ .addPaths("")
+ .addPaths("foo")
+ .addPaths("")
+ .addPaths("bar")
+ .addPaths("")
+ .build();
assertEquals("foo,bar", FieldMaskUtil.toString(mask));
}
@@ -111,8 +114,7 @@ public class FieldMaskUtilTest extends TestCase {
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload");
try {
- mask = FieldMaskUtil.fromString(
- NestedTestAllTypes.class, "payload,nonexist");
+ mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, "payload,nonexist");
fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
@@ -143,7 +145,33 @@ public class FieldMaskUtilTest extends TestCase {
} catch (IllegalArgumentException expected) {
}
}
-
+
+ public void testToJsonString() throws Exception {
+ FieldMask mask = FieldMask.getDefaultInstance();
+ assertEquals("", FieldMaskUtil.toJsonString(mask));
+ mask = FieldMask.newBuilder().addPaths("foo").build();
+ assertEquals("foo", FieldMaskUtil.toJsonString(mask));
+ mask = FieldMask.newBuilder().addPaths("foo.bar_baz").addPaths("").build();
+ assertEquals("foo.barBaz", FieldMaskUtil.toJsonString(mask));
+ mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar_baz").build();
+ assertEquals("foo,barBaz", FieldMaskUtil.toJsonString(mask));
+ }
+
+ public void testFromJsonString() throws Exception {
+ FieldMask mask = FieldMaskUtil.fromJsonString("");
+ assertEquals(0, mask.getPathsCount());
+ mask = FieldMaskUtil.fromJsonString("foo");
+ assertEquals(1, mask.getPathsCount());
+ assertEquals("foo", mask.getPaths(0));
+ mask = FieldMaskUtil.fromJsonString("foo.barBaz");
+ assertEquals(1, mask.getPathsCount());
+ assertEquals("foo.bar_baz", mask.getPaths(0));
+ mask = FieldMaskUtil.fromJsonString("foo,barBaz");
+ assertEquals(2, mask.getPathsCount());
+ assertEquals("foo", mask.getPaths(0));
+ assertEquals("bar_baz", mask.getPaths(1));
+ }
+
public void testUnion() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios.
@@ -161,7 +189,7 @@ public class FieldMaskUtilTest extends TestCase {
FieldMask result = FieldMaskUtil.union(mask1, mask2, mask3, mask4);
assertEquals("bar,foo", FieldMaskUtil.toString(result));
}
-
+
public void testIntersection() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testIntersectFieldPath} to cover all scenarios.
@@ -170,13 +198,14 @@ public class FieldMaskUtilTest extends TestCase {
FieldMask result = FieldMaskUtil.intersection(mask1, mask2);
assertEquals("bar.baz,bar.quz,foo.bar", FieldMaskUtil.toString(result));
}
-
+
public void testMerge() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testMerge} to cover all scenarios.
- NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
- .setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
- .build();
+ NestedTestAllTypes source =
+ NestedTestAllTypes.newBuilder()
+ .setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
+ .build();
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
FieldMaskUtil.merge(FieldMaskUtil.fromString("payload"), source, builder);
assertEquals(1234, builder.getPayload().getOptionalInt32());
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 d95b626c..4d9a417d 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
@@ -41,6 +41,7 @@ import com.google.protobuf.Int64Value;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue;
import com.google.protobuf.Message;
+import com.google.protobuf.NullValue;
import com.google.protobuf.StringValue;
import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value;
@@ -82,7 +83,7 @@ public class JsonFormatTest extends TestCase {
builder.setOptionalDouble(1.25);
builder.setOptionalBool(true);
builder.setOptionalString("Hello world!");
- builder.setOptionalBytes(ByteString.copyFrom(new byte[]{0, 1, 2}));
+ builder.setOptionalBytes(ByteString.copyFrom(new byte[] {0, 1, 2}));
builder.setOptionalNestedEnum(NestedEnum.BAR);
builder.getOptionalNestedMessageBuilder().setValue(100);
@@ -100,7 +101,7 @@ public class JsonFormatTest extends TestCase {
builder.addRepeatedDouble(1.25);
builder.addRepeatedBool(true);
builder.addRepeatedString("Hello world!");
- builder.addRepeatedBytes(ByteString.copyFrom(new byte[]{0, 1, 2}));
+ builder.addRepeatedBytes(ByteString.copyFrom(new byte[] {0, 1, 2}));
builder.addRepeatedNestedEnum(NestedEnum.BAR);
builder.addRepeatedNestedMessageBuilder().setValue(100);
@@ -118,7 +119,7 @@ public class JsonFormatTest extends TestCase {
builder.addRepeatedDouble(11.25);
builder.addRepeatedBool(true);
builder.addRepeatedString("ello world!");
- builder.addRepeatedBytes(ByteString.copyFrom(new byte[]{1, 2}));
+ builder.addRepeatedBytes(ByteString.copyFrom(new byte[] {1, 2}));
builder.addRepeatedNestedEnum(NestedEnum.BAZ);
builder.addRepeatedNestedMessageBuilder().setValue(200);
}
@@ -198,20 +199,22 @@ public class JsonFormatTest extends TestCase {
}
public void testUnknownEnumValues() throws Exception {
- TestAllTypes message = TestAllTypes.newBuilder()
- .setOptionalNestedEnumValue(12345)
- .addRepeatedNestedEnumValue(12345)
- .addRepeatedNestedEnumValue(0)
- .build();
+ TestAllTypes message =
+ TestAllTypes.newBuilder()
+ .setOptionalNestedEnumValue(12345)
+ .addRepeatedNestedEnumValue(12345)
+ .addRepeatedNestedEnumValue(0)
+ .build();
assertEquals(
"{\n"
- + " \"optionalNestedEnum\": 12345,\n"
- + " \"repeatedNestedEnum\": [12345, \"FOO\"]\n"
- + "}", toJsonString(message));
+ + " \"optionalNestedEnum\": 12345,\n"
+ + " \"repeatedNestedEnum\": [12345, \"FOO\"]\n"
+ + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
TestMap.Builder mapBuilder = TestMap.newBuilder();
- mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0);
+ mapBuilder.putInt32ToEnumMapValue(1, 0);
mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345);
TestMap mapMessage = mapBuilder.build();
assertEquals(
@@ -226,19 +229,21 @@ public class JsonFormatTest extends TestCase {
}
public void testSpecialFloatValues() throws Exception {
- TestAllTypes message = TestAllTypes.newBuilder()
- .addRepeatedFloat(Float.NaN)
- .addRepeatedFloat(Float.POSITIVE_INFINITY)
- .addRepeatedFloat(Float.NEGATIVE_INFINITY)
- .addRepeatedDouble(Double.NaN)
- .addRepeatedDouble(Double.POSITIVE_INFINITY)
- .addRepeatedDouble(Double.NEGATIVE_INFINITY)
- .build();
+ TestAllTypes message =
+ TestAllTypes.newBuilder()
+ .addRepeatedFloat(Float.NaN)
+ .addRepeatedFloat(Float.POSITIVE_INFINITY)
+ .addRepeatedFloat(Float.NEGATIVE_INFINITY)
+ .addRepeatedDouble(Double.NaN)
+ .addRepeatedDouble(Double.POSITIVE_INFINITY)
+ .addRepeatedDouble(Double.NEGATIVE_INFINITY)
+ .build();
assertEquals(
"{\n"
- + " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n"
- + " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n"
- + "}", toJsonString(message));
+ + " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n"
+ + " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n"
+ + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
}
@@ -247,15 +252,16 @@ public class JsonFormatTest extends TestCase {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
- + " \"optionalInt32\": \"1234\",\n"
- + " \"optionalUint32\": \"5678\",\n"
- + " \"optionalSint32\": \"9012\",\n"
- + " \"optionalFixed32\": \"3456\",\n"
- + " \"optionalSfixed32\": \"7890\",\n"
- + " \"optionalFloat\": \"1.5\",\n"
- + " \"optionalDouble\": \"1.25\",\n"
- + " \"optionalBool\": \"true\"\n"
- + "}", builder);
+ + " \"optionalInt32\": \"1234\",\n"
+ + " \"optionalUint32\": \"5678\",\n"
+ + " \"optionalSint32\": \"9012\",\n"
+ + " \"optionalFixed32\": \"3456\",\n"
+ + " \"optionalSfixed32\": \"7890\",\n"
+ + " \"optionalFloat\": \"1.5\",\n"
+ + " \"optionalDouble\": \"1.25\",\n"
+ + " \"optionalBool\": \"true\"\n"
+ + "}",
+ builder);
TestAllTypes message = builder.build();
assertEquals(1234, message.getOptionalInt32());
assertEquals(5678, message.getOptionalUint32());
@@ -276,8 +282,9 @@ public class JsonFormatTest extends TestCase {
+ " \"repeatedUint32\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ " \"repeatedInt64\": [1.000, 1e5, \"1.000\", \"1e5\"],\n"
+ " \"repeatedUint64\": [1.000, 1e5, \"1.000\", \"1e5\"]\n"
- + "}", builder);
- int[] expectedValues = new int[]{1, 100000, 1, 100000};
+ + "}",
+ builder);
+ int[] expectedValues = new int[] {1, 100000, 1, 100000};
assertEquals(4, builder.getRepeatedInt32Count());
assertEquals(4, builder.getRepeatedUint32Count());
assertEquals(4, builder.getRepeatedInt64Count());
@@ -366,51 +373,49 @@ public class JsonFormatTest extends TestCase {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
- + " \"optionalInt32\": null,\n"
- + " \"optionalInt64\": null,\n"
- + " \"optionalUint32\": null,\n"
- + " \"optionalUint64\": null,\n"
- + " \"optionalSint32\": null,\n"
- + " \"optionalSint64\": null,\n"
- + " \"optionalFixed32\": null,\n"
- + " \"optionalFixed64\": null,\n"
- + " \"optionalSfixed32\": null,\n"
- + " \"optionalSfixed64\": null,\n"
- + " \"optionalFloat\": null,\n"
- + " \"optionalDouble\": null,\n"
- + " \"optionalBool\": null,\n"
- + " \"optionalString\": null,\n"
- + " \"optionalBytes\": null,\n"
- + " \"optionalNestedMessage\": null,\n"
- + " \"optionalNestedEnum\": null,\n"
- + " \"repeatedInt32\": null,\n"
- + " \"repeatedInt64\": null,\n"
- + " \"repeatedUint32\": null,\n"
- + " \"repeatedUint64\": null,\n"
- + " \"repeatedSint32\": null,\n"
- + " \"repeatedSint64\": null,\n"
- + " \"repeatedFixed32\": null,\n"
- + " \"repeatedFixed64\": null,\n"
- + " \"repeatedSfixed32\": null,\n"
- + " \"repeatedSfixed64\": null,\n"
- + " \"repeatedFloat\": null,\n"
- + " \"repeatedDouble\": null,\n"
- + " \"repeatedBool\": null,\n"
- + " \"repeatedString\": null,\n"
- + " \"repeatedBytes\": null,\n"
- + " \"repeatedNestedMessage\": null,\n"
- + " \"repeatedNestedEnum\": null\n"
- + "}", builder);
+ + " \"optionalInt32\": null,\n"
+ + " \"optionalInt64\": null,\n"
+ + " \"optionalUint32\": null,\n"
+ + " \"optionalUint64\": null,\n"
+ + " \"optionalSint32\": null,\n"
+ + " \"optionalSint64\": null,\n"
+ + " \"optionalFixed32\": null,\n"
+ + " \"optionalFixed64\": null,\n"
+ + " \"optionalSfixed32\": null,\n"
+ + " \"optionalSfixed64\": null,\n"
+ + " \"optionalFloat\": null,\n"
+ + " \"optionalDouble\": null,\n"
+ + " \"optionalBool\": null,\n"
+ + " \"optionalString\": null,\n"
+ + " \"optionalBytes\": null,\n"
+ + " \"optionalNestedMessage\": null,\n"
+ + " \"optionalNestedEnum\": null,\n"
+ + " \"repeatedInt32\": null,\n"
+ + " \"repeatedInt64\": null,\n"
+ + " \"repeatedUint32\": null,\n"
+ + " \"repeatedUint64\": null,\n"
+ + " \"repeatedSint32\": null,\n"
+ + " \"repeatedSint64\": null,\n"
+ + " \"repeatedFixed32\": null,\n"
+ + " \"repeatedFixed64\": null,\n"
+ + " \"repeatedSfixed32\": null,\n"
+ + " \"repeatedSfixed64\": null,\n"
+ + " \"repeatedFloat\": null,\n"
+ + " \"repeatedDouble\": null,\n"
+ + " \"repeatedBool\": null,\n"
+ + " \"repeatedString\": null,\n"
+ + " \"repeatedBytes\": null,\n"
+ + " \"repeatedNestedMessage\": null,\n"
+ + " \"repeatedNestedEnum\": null\n"
+ + "}",
+ builder);
TestAllTypes message = builder.build();
assertEquals(TestAllTypes.getDefaultInstance(), message);
// Repeated field elements cannot be null.
try {
builder = TestAllTypes.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"repeatedInt32\": [null, null],\n"
- + "}", builder);
+ mergeFromJson("{\n" + " \"repeatedInt32\": [null, null],\n" + "}", builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
@@ -418,16 +423,21 @@ public class JsonFormatTest extends TestCase {
try {
builder = TestAllTypes.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"repeatedNestedMessage\": [null, null],\n"
- + "}", builder);
+ mergeFromJson("{\n" + " \"repeatedNestedMessage\": [null, null],\n" + "}", builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
}
}
+ public void testNullInOneof() throws Exception {
+ TestOneof.Builder builder = TestOneof.newBuilder();
+ mergeFromJson("{\n" + " \"oneofNullValue\": null \n" + "}", builder);
+ TestOneof message = builder.build();
+ assertEquals(TestOneof.OneofFieldCase.ONEOF_NULL_VALUE, message.getOneofFieldCase());
+ assertEquals(NullValue.NULL_VALUE, message.getOneofNullValue());
+ }
+
public void testParserRejectDuplicatedFields() throws Exception {
// TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last
// one if multiple entries have the same name. This is not the desired behavior but it can
@@ -441,7 +451,8 @@ public class JsonFormatTest extends TestCase {
"{\n"
+ " \"optionalNestedMessage\": {},\n"
+ " \"optional_nested_message\": {}\n"
- + "}", builder);
+ + "}",
+ builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
@@ -452,9 +463,10 @@ public class JsonFormatTest extends TestCase {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
- + " \"repeatedNestedMessage\": [null, null],\n"
- + " \"repeated_nested_message\": [null, null]\n"
- + "}", builder);
+ + " \"repeatedNestedMessage\": [null, null],\n"
+ + " \"repeated_nested_message\": [null, null]\n"
+ + "}",
+ builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
@@ -463,11 +475,7 @@ public class JsonFormatTest extends TestCase {
// Duplicated oneof fields.
try {
TestOneof.Builder builder = TestOneof.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"oneofInt32\": 1,\n"
- + " \"oneof_int32\": 2\n"
- + "}", builder);
+ mergeFromJson("{\n" + " \"oneofInt32\": 1,\n" + " \"oneof_int32\": 2\n" + "}", builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
@@ -476,143 +484,138 @@ public class JsonFormatTest extends TestCase {
public void testMapFields() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToInt32Map().put(1, 10);
- builder.getMutableInt64ToInt32Map().put(1234567890123456789L, 10);
- builder.getMutableUint32ToInt32Map().put(2, 20);
- builder.getMutableUint64ToInt32Map().put(2234567890123456789L, 20);
- builder.getMutableSint32ToInt32Map().put(3, 30);
- builder.getMutableSint64ToInt32Map().put(3234567890123456789L, 30);
- builder.getMutableFixed32ToInt32Map().put(4, 40);
- builder.getMutableFixed64ToInt32Map().put(4234567890123456789L, 40);
- builder.getMutableSfixed32ToInt32Map().put(5, 50);
- builder.getMutableSfixed64ToInt32Map().put(5234567890123456789L, 50);
- builder.getMutableBoolToInt32Map().put(false, 6);
- builder.getMutableStringToInt32Map().put("Hello", 10);
-
- builder.getMutableInt32ToInt64Map().put(1, 1234567890123456789L);
- builder.getMutableInt32ToUint32Map().put(2, 20);
- builder.getMutableInt32ToUint64Map().put(2, 2234567890123456789L);
- builder.getMutableInt32ToSint32Map().put(3, 30);
- builder.getMutableInt32ToSint64Map().put(3, 3234567890123456789L);
- builder.getMutableInt32ToFixed32Map().put(4, 40);
- builder.getMutableInt32ToFixed64Map().put(4, 4234567890123456789L);
- builder.getMutableInt32ToSfixed32Map().put(5, 50);
- builder.getMutableInt32ToSfixed64Map().put(5, 5234567890123456789L);
- builder.getMutableInt32ToFloatMap().put(6, 1.5f);
- builder.getMutableInt32ToDoubleMap().put(6, 1.25);
- builder.getMutableInt32ToBoolMap().put(7, false);
- builder.getMutableInt32ToStringMap().put(7, "World");
- builder.getMutableInt32ToBytesMap().put(
- 8, ByteString.copyFrom(new byte[]{1, 2, 3}));
- builder.getMutableInt32ToMessageMap().put(
- 8, NestedMessage.newBuilder().setValue(1234).build());
- builder.getMutableInt32ToEnumMap().put(9, NestedEnum.BAR);
+ builder.putInt32ToInt32Map(1, 10);
+ builder.putInt64ToInt32Map(1234567890123456789L, 10);
+ builder.putUint32ToInt32Map(2, 20);
+ builder.putUint64ToInt32Map(2234567890123456789L, 20);
+ builder.putSint32ToInt32Map(3, 30);
+ builder.putSint64ToInt32Map(3234567890123456789L, 30);
+ builder.putFixed32ToInt32Map(4, 40);
+ builder.putFixed64ToInt32Map(4234567890123456789L, 40);
+ builder.putSfixed32ToInt32Map(5, 50);
+ builder.putSfixed64ToInt32Map(5234567890123456789L, 50);
+ builder.putBoolToInt32Map(false, 6);
+ builder.putStringToInt32Map("Hello", 10);
+
+ builder.putInt32ToInt64Map(1, 1234567890123456789L);
+ builder.putInt32ToUint32Map(2, 20);
+ builder.putInt32ToUint64Map(2, 2234567890123456789L);
+ builder.putInt32ToSint32Map(3, 30);
+ builder.putInt32ToSint64Map(3, 3234567890123456789L);
+ builder.putInt32ToFixed32Map(4, 40);
+ builder.putInt32ToFixed64Map(4, 4234567890123456789L);
+ builder.putInt32ToSfixed32Map(5, 50);
+ builder.putInt32ToSfixed64Map(5, 5234567890123456789L);
+ builder.putInt32ToFloatMap(6, 1.5f);
+ builder.putInt32ToDoubleMap(6, 1.25);
+ builder.putInt32ToBoolMap(7, false);
+ builder.putInt32ToStringMap(7, "World");
+ builder.putInt32ToBytesMap(8, ByteString.copyFrom(new byte[] {1, 2, 3}));
+ builder.putInt32ToMessageMap(8, NestedMessage.newBuilder().setValue(1234).build());
+ builder.putInt32ToEnumMap(9, NestedEnum.BAR);
TestMap message = builder.build();
assertEquals(
"{\n"
- + " \"int32ToInt32Map\": {\n"
- + " \"1\": 10\n"
- + " },\n"
- + " \"int64ToInt32Map\": {\n"
- + " \"1234567890123456789\": 10\n"
- + " },\n"
- + " \"uint32ToInt32Map\": {\n"
- + " \"2\": 20\n"
- + " },\n"
- + " \"uint64ToInt32Map\": {\n"
- + " \"2234567890123456789\": 20\n"
- + " },\n"
- + " \"sint32ToInt32Map\": {\n"
- + " \"3\": 30\n"
- + " },\n"
- + " \"sint64ToInt32Map\": {\n"
- + " \"3234567890123456789\": 30\n"
- + " },\n"
- + " \"fixed32ToInt32Map\": {\n"
- + " \"4\": 40\n"
- + " },\n"
- + " \"fixed64ToInt32Map\": {\n"
- + " \"4234567890123456789\": 40\n"
- + " },\n"
- + " \"sfixed32ToInt32Map\": {\n"
- + " \"5\": 50\n"
- + " },\n"
- + " \"sfixed64ToInt32Map\": {\n"
- + " \"5234567890123456789\": 50\n"
- + " },\n"
- + " \"boolToInt32Map\": {\n"
- + " \"false\": 6\n"
- + " },\n"
- + " \"stringToInt32Map\": {\n"
- + " \"Hello\": 10\n"
- + " },\n"
- + " \"int32ToInt64Map\": {\n"
- + " \"1\": \"1234567890123456789\"\n"
- + " },\n"
- + " \"int32ToUint32Map\": {\n"
- + " \"2\": 20\n"
- + " },\n"
- + " \"int32ToUint64Map\": {\n"
- + " \"2\": \"2234567890123456789\"\n"
- + " },\n"
- + " \"int32ToSint32Map\": {\n"
- + " \"3\": 30\n"
- + " },\n"
- + " \"int32ToSint64Map\": {\n"
- + " \"3\": \"3234567890123456789\"\n"
- + " },\n"
- + " \"int32ToFixed32Map\": {\n"
- + " \"4\": 40\n"
- + " },\n"
- + " \"int32ToFixed64Map\": {\n"
- + " \"4\": \"4234567890123456789\"\n"
- + " },\n"
- + " \"int32ToSfixed32Map\": {\n"
- + " \"5\": 50\n"
- + " },\n"
- + " \"int32ToSfixed64Map\": {\n"
- + " \"5\": \"5234567890123456789\"\n"
- + " },\n"
- + " \"int32ToFloatMap\": {\n"
- + " \"6\": 1.5\n"
- + " },\n"
- + " \"int32ToDoubleMap\": {\n"
- + " \"6\": 1.25\n"
- + " },\n"
- + " \"int32ToBoolMap\": {\n"
- + " \"7\": false\n"
- + " },\n"
- + " \"int32ToStringMap\": {\n"
- + " \"7\": \"World\"\n"
- + " },\n"
- + " \"int32ToBytesMap\": {\n"
- + " \"8\": \"AQID\"\n"
- + " },\n"
- + " \"int32ToMessageMap\": {\n"
- + " \"8\": {\n"
- + " \"value\": 1234\n"
- + " }\n"
- + " },\n"
- + " \"int32ToEnumMap\": {\n"
- + " \"9\": \"BAR\"\n"
- + " }\n"
- + "}", toJsonString(message));
+ + " \"int32ToInt32Map\": {\n"
+ + " \"1\": 10\n"
+ + " },\n"
+ + " \"int64ToInt32Map\": {\n"
+ + " \"1234567890123456789\": 10\n"
+ + " },\n"
+ + " \"uint32ToInt32Map\": {\n"
+ + " \"2\": 20\n"
+ + " },\n"
+ + " \"uint64ToInt32Map\": {\n"
+ + " \"2234567890123456789\": 20\n"
+ + " },\n"
+ + " \"sint32ToInt32Map\": {\n"
+ + " \"3\": 30\n"
+ + " },\n"
+ + " \"sint64ToInt32Map\": {\n"
+ + " \"3234567890123456789\": 30\n"
+ + " },\n"
+ + " \"fixed32ToInt32Map\": {\n"
+ + " \"4\": 40\n"
+ + " },\n"
+ + " \"fixed64ToInt32Map\": {\n"
+ + " \"4234567890123456789\": 40\n"
+ + " },\n"
+ + " \"sfixed32ToInt32Map\": {\n"
+ + " \"5\": 50\n"
+ + " },\n"
+ + " \"sfixed64ToInt32Map\": {\n"
+ + " \"5234567890123456789\": 50\n"
+ + " },\n"
+ + " \"boolToInt32Map\": {\n"
+ + " \"false\": 6\n"
+ + " },\n"
+ + " \"stringToInt32Map\": {\n"
+ + " \"Hello\": 10\n"
+ + " },\n"
+ + " \"int32ToInt64Map\": {\n"
+ + " \"1\": \"1234567890123456789\"\n"
+ + " },\n"
+ + " \"int32ToUint32Map\": {\n"
+ + " \"2\": 20\n"
+ + " },\n"
+ + " \"int32ToUint64Map\": {\n"
+ + " \"2\": \"2234567890123456789\"\n"
+ + " },\n"
+ + " \"int32ToSint32Map\": {\n"
+ + " \"3\": 30\n"
+ + " },\n"
+ + " \"int32ToSint64Map\": {\n"
+ + " \"3\": \"3234567890123456789\"\n"
+ + " },\n"
+ + " \"int32ToFixed32Map\": {\n"
+ + " \"4\": 40\n"
+ + " },\n"
+ + " \"int32ToFixed64Map\": {\n"
+ + " \"4\": \"4234567890123456789\"\n"
+ + " },\n"
+ + " \"int32ToSfixed32Map\": {\n"
+ + " \"5\": 50\n"
+ + " },\n"
+ + " \"int32ToSfixed64Map\": {\n"
+ + " \"5\": \"5234567890123456789\"\n"
+ + " },\n"
+ + " \"int32ToFloatMap\": {\n"
+ + " \"6\": 1.5\n"
+ + " },\n"
+ + " \"int32ToDoubleMap\": {\n"
+ + " \"6\": 1.25\n"
+ + " },\n"
+ + " \"int32ToBoolMap\": {\n"
+ + " \"7\": false\n"
+ + " },\n"
+ + " \"int32ToStringMap\": {\n"
+ + " \"7\": \"World\"\n"
+ + " },\n"
+ + " \"int32ToBytesMap\": {\n"
+ + " \"8\": \"AQID\"\n"
+ + " },\n"
+ + " \"int32ToMessageMap\": {\n"
+ + " \"8\": {\n"
+ + " \"value\": 1234\n"
+ + " }\n"
+ + " },\n"
+ + " \"int32ToEnumMap\": {\n"
+ + " \"9\": \"BAR\"\n"
+ + " }\n"
+ + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
// Test multiple entries.
builder = TestMap.newBuilder();
- builder.getMutableInt32ToInt32Map().put(1, 2);
- builder.getMutableInt32ToInt32Map().put(3, 4);
+ builder.putInt32ToInt32Map(1, 2);
+ builder.putInt32ToInt32Map(3, 4);
message = builder.build();
assertEquals(
- "{\n"
- + " \"int32ToInt32Map\": {\n"
- + " \"1\": 2,\n"
- + " \"3\": 4\n"
- + " }\n"
- + "}", toJsonString(message));
+ "{\n" + " \"int32ToInt32Map\": {\n" + " \"1\": 2,\n" + " \"3\": 4\n" + " }\n" + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
}
@@ -621,9 +624,10 @@ public class JsonFormatTest extends TestCase {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
"{\n"
- + " \"int32ToInt32Map\": {null: 1},\n"
- + " \"int32ToMessageMap\": {null: 2}\n"
- + "}", builder);
+ + " \"int32ToInt32Map\": {null: 1},\n"
+ + " \"int32ToMessageMap\": {null: 2}\n"
+ + "}",
+ builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
@@ -633,9 +637,10 @@ public class JsonFormatTest extends TestCase {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
"{\n"
- + " \"int32ToInt32Map\": {\"1\": null},\n"
- + " \"int32ToMessageMap\": {\"2\": null}\n"
- + "}", builder);
+ + " \"int32ToInt32Map\": {\"1\": null},\n"
+ + " \"int32ToMessageMap\": {\"2\": null}\n"
+ + "}",
+ builder);
fail();
} catch (InvalidProtocolBufferException e) {
// Exception expected.
@@ -645,10 +650,7 @@ public class JsonFormatTest extends TestCase {
public void testParserAcceptNonQuotedObjectKey() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
- "{\n"
- + " int32ToInt32Map: {1: 2},\n"
- + " stringToInt32Map: {hello: 3}\n"
- + "}", builder);
+ "{\n" + " int32ToInt32Map: {1: 2},\n" + " stringToInt32Map: {hello: 3}\n" + "}", builder);
TestMap message = builder.build();
assertEquals(2, message.getInt32ToInt32Map().get(1).intValue());
assertEquals(3, message.getStringToInt32Map().get("hello").intValue());
@@ -669,16 +671,17 @@ public class JsonFormatTest extends TestCase {
assertEquals(
"{\n"
- + " \"int32Value\": 0,\n"
- + " \"uint32Value\": 0,\n"
- + " \"int64Value\": \"0\",\n"
- + " \"uint64Value\": \"0\",\n"
- + " \"floatValue\": 0.0,\n"
- + " \"doubleValue\": 0.0,\n"
- + " \"boolValue\": false,\n"
- + " \"stringValue\": \"\",\n"
- + " \"bytesValue\": \"\"\n"
- + "}", toJsonString(message));
+ + " \"int32Value\": 0,\n"
+ + " \"uint32Value\": 0,\n"
+ + " \"int64Value\": \"0\",\n"
+ + " \"uint64Value\": \"0\",\n"
+ + " \"floatValue\": 0.0,\n"
+ + " \"doubleValue\": 0.0,\n"
+ + " \"boolValue\": false,\n"
+ + " \"stringValue\": \"\",\n"
+ + " \"bytesValue\": \"\"\n"
+ + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
builder = TestWrappers.newBuilder();
@@ -690,57 +693,52 @@ public class JsonFormatTest extends TestCase {
builder.getFloatValueBuilder().setValue(5.0f);
builder.getDoubleValueBuilder().setValue(6.0);
builder.getStringValueBuilder().setValue("7");
- builder.getBytesValueBuilder().setValue(ByteString.copyFrom(new byte[]{8}));
+ builder.getBytesValueBuilder().setValue(ByteString.copyFrom(new byte[] {8}));
message = builder.build();
assertEquals(
"{\n"
- + " \"int32Value\": 1,\n"
- + " \"uint32Value\": 3,\n"
- + " \"int64Value\": \"2\",\n"
- + " \"uint64Value\": \"4\",\n"
- + " \"floatValue\": 5.0,\n"
- + " \"doubleValue\": 6.0,\n"
- + " \"boolValue\": true,\n"
- + " \"stringValue\": \"7\",\n"
- + " \"bytesValue\": \"CA==\"\n"
- + "}", toJsonString(message));
+ + " \"int32Value\": 1,\n"
+ + " \"uint32Value\": 3,\n"
+ + " \"int64Value\": \"2\",\n"
+ + " \"uint64Value\": \"4\",\n"
+ + " \"floatValue\": 5.0,\n"
+ + " \"doubleValue\": 6.0,\n"
+ + " \"boolValue\": true,\n"
+ + " \"stringValue\": \"7\",\n"
+ + " \"bytesValue\": \"CA==\"\n"
+ + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
}
public void testTimestamp() throws Exception {
- TestTimestamp message = TestTimestamp.newBuilder()
- .setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z"))
- .build();
+ TestTimestamp message =
+ TestTimestamp.newBuilder()
+ .setTimestampValue(Timestamps.parse("1970-01-01T00:00:00Z"))
+ .build();
assertEquals(
- "{\n"
- + " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n"
- + "}", toJsonString(message));
+ "{\n" + " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n" + "}", toJsonString(message));
assertRoundTripEquals(message);
}
public void testDuration() throws Exception {
- TestDuration message = TestDuration.newBuilder()
- .setDurationValue(TimeUtil.parseDuration("12345s"))
- .build();
+ TestDuration message =
+ TestDuration.newBuilder().setDurationValue(Durations.parse("12345s")).build();
- assertEquals(
- "{\n"
- + " \"durationValue\": \"12345s\"\n"
- + "}", toJsonString(message));
+ assertEquals("{\n" + " \"durationValue\": \"12345s\"\n" + "}", toJsonString(message));
assertRoundTripEquals(message);
}
public void testFieldMask() throws Exception {
- TestFieldMask message = TestFieldMask.newBuilder()
- .setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz"))
- .build();
+ TestFieldMask message =
+ TestFieldMask.newBuilder()
+ .setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz,foo_bar.baz"))
+ .build();
assertEquals(
- "{\n"
- + " \"fieldMaskValue\": \"foo.bar,baz\"\n"
- + "}", toJsonString(message));
+ "{\n" + " \"fieldMaskValue\": \"foo.bar,baz,fooBar.baz\"\n" + "}", toJsonString(message));
assertRoundTripEquals(message);
}
@@ -748,45 +746,39 @@ public class JsonFormatTest extends TestCase {
// Build a struct with all possible values.
TestStruct.Builder builder = TestStruct.newBuilder();
Struct.Builder structBuilder = builder.getStructValueBuilder();
- structBuilder.getMutableFields().put(
- "null_value", Value.newBuilder().setNullValueValue(0).build());
- structBuilder.getMutableFields().put(
- "number_value", Value.newBuilder().setNumberValue(1.25).build());
- structBuilder.getMutableFields().put(
- "string_value", Value.newBuilder().setStringValue("hello").build());
+ structBuilder.putFields("null_value", Value.newBuilder().setNullValueValue(0).build());
+ structBuilder.putFields("number_value", Value.newBuilder().setNumberValue(1.25).build());
+ structBuilder.putFields("string_value", Value.newBuilder().setStringValue("hello").build());
Struct.Builder subStructBuilder = Struct.newBuilder();
- subStructBuilder.getMutableFields().put(
- "number_value", Value.newBuilder().setNumberValue(1234).build());
- structBuilder.getMutableFields().put(
+ subStructBuilder.putFields("number_value", Value.newBuilder().setNumberValue(1234).build());
+ structBuilder.putFields(
"struct_value", Value.newBuilder().setStructValue(subStructBuilder.build()).build());
ListValue.Builder listBuilder = ListValue.newBuilder();
listBuilder.addValues(Value.newBuilder().setNumberValue(1.125).build());
listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build());
- structBuilder.getMutableFields().put(
+ structBuilder.putFields(
"list_value", Value.newBuilder().setListValue(listBuilder.build()).build());
TestStruct message = builder.build();
assertEquals(
"{\n"
- + " \"structValue\": {\n"
- + " \"null_value\": null,\n"
- + " \"number_value\": 1.25,\n"
- + " \"string_value\": \"hello\",\n"
- + " \"struct_value\": {\n"
- + " \"number_value\": 1234.0\n"
- + " },\n"
- + " \"list_value\": [1.125, null]\n"
- + " }\n"
- + "}", toJsonString(message));
+ + " \"structValue\": {\n"
+ + " \"null_value\": null,\n"
+ + " \"number_value\": 1.25,\n"
+ + " \"string_value\": \"hello\",\n"
+ + " \"struct_value\": {\n"
+ + " \"number_value\": 1234.0\n"
+ + " },\n"
+ + " \"list_value\": [1.125, null]\n"
+ + " }\n"
+ + "}",
+ toJsonString(message));
assertRoundTripEquals(message);
builder = TestStruct.newBuilder();
builder.setValue(Value.newBuilder().setNullValueValue(0).build());
message = builder.build();
- assertEquals(
- "{\n"
- + " \"value\": null\n"
- + "}", toJsonString(message));
+ assertEquals("{\n" + " \"value\": null\n" + "}", toJsonString(message));
assertRoundTripEquals(message);
builder = TestStruct.newBuilder();
@@ -794,10 +786,7 @@ public class JsonFormatTest extends TestCase {
listBuilder.addValues(Value.newBuilder().setNumberValue(31831.125).build());
listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build());
message = builder.build();
- assertEquals(
- "{\n"
- + " \"listValue\": [31831.125, null]\n"
- + "}", toJsonString(message));
+ assertEquals("{\n" + " \"listValue\": [31831.125, null]\n" + "}", toJsonString(message));
assertRoundTripEquals(message);
}
@@ -813,158 +802,169 @@ public class JsonFormatTest extends TestCase {
// Expected.
}
- JsonFormat.TypeRegistry registry = JsonFormat.TypeRegistry.newBuilder()
- .add(TestAllTypes.getDescriptor()).build();
+ JsonFormat.TypeRegistry registry =
+ JsonFormat.TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build();
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
assertEquals(
"{\n"
- + " \"anyValue\": {\n"
- + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
- + " \"optionalInt32\": 1234\n"
- + " }\n"
- + "}" , printer.print(message));
+ + " \"anyValue\": {\n"
+ + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ + " \"optionalInt32\": 1234\n"
+ + " }\n"
+ + "}",
+ printer.print(message));
assertRoundTripEquals(message, registry);
-
// Well-known types have a special formatting when embedded in Any.
//
// 1. Any in Any.
Any anyMessage = Any.pack(Any.pack(content));
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n"
- + " \"value\": {\n"
- + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
- + " \"optionalInt32\": 1234\n"
- + " }\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n"
+ + " \"value\": {\n"
+ + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ + " \"optionalInt32\": 1234\n"
+ + " }\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
// 2. Wrappers in Any.
anyMessage = Any.pack(Int32Value.newBuilder().setValue(12345).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Int32Value\",\n"
- + " \"value\": 12345\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Int32Value\",\n"
+ + " \"value\": 12345\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(UInt32Value.newBuilder().setValue(12345).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.UInt32Value\",\n"
- + " \"value\": 12345\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.UInt32Value\",\n"
+ + " \"value\": 12345\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(Int64Value.newBuilder().setValue(12345).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Int64Value\",\n"
- + " \"value\": \"12345\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Int64Value\",\n"
+ + " \"value\": \"12345\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(UInt64Value.newBuilder().setValue(12345).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.UInt64Value\",\n"
- + " \"value\": \"12345\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.UInt64Value\",\n"
+ + " \"value\": \"12345\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(FloatValue.newBuilder().setValue(12345).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.FloatValue\",\n"
- + " \"value\": 12345.0\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.FloatValue\",\n"
+ + " \"value\": 12345.0\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(DoubleValue.newBuilder().setValue(12345).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.DoubleValue\",\n"
- + " \"value\": 12345.0\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.DoubleValue\",\n"
+ + " \"value\": 12345.0\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(BoolValue.newBuilder().setValue(true).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.BoolValue\",\n"
- + " \"value\": true\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.BoolValue\",\n"
+ + " \"value\": true\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
anyMessage = Any.pack(StringValue.newBuilder().setValue("Hello").build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.StringValue\",\n"
- + " \"value\": \"Hello\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.StringValue\",\n"
+ + " \"value\": \"Hello\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
- anyMessage = Any.pack(BytesValue.newBuilder().setValue(
- ByteString.copyFrom(new byte[]{1, 2})).build());
+ anyMessage =
+ Any.pack(BytesValue.newBuilder().setValue(ByteString.copyFrom(new byte[] {1, 2})).build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.BytesValue\",\n"
- + " \"value\": \"AQI=\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.BytesValue\",\n"
+ + " \"value\": \"AQI=\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
// 3. Timestamp in Any.
- anyMessage = Any.pack(TimeUtil.parseTimestamp("1969-12-31T23:59:59Z"));
+ anyMessage = Any.pack(Timestamps.parse("1969-12-31T23:59:59Z"));
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\",\n"
- + " \"value\": \"1969-12-31T23:59:59Z\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\",\n"
+ + " \"value\": \"1969-12-31T23:59:59Z\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
// 4. Duration in Any
- anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s"));
+ anyMessage = Any.pack(Durations.parse("12345.10s"));
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n"
- + " \"value\": \"12345.100s\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n"
+ + " \"value\": \"12345.100s\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
// 5. FieldMask in Any
anyMessage = Any.pack(FieldMaskUtil.fromString("foo.bar,baz"));
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.FieldMask\",\n"
- + " \"value\": \"foo.bar,baz\"\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.FieldMask\",\n"
+ + " \"value\": \"foo.bar,baz\"\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
// 6. Struct in Any
Struct.Builder structBuilder = Struct.newBuilder();
- structBuilder.getMutableFields().put(
- "number", Value.newBuilder().setNumberValue(1.125).build());
+ structBuilder.putFields("number", Value.newBuilder().setNumberValue(1.125).build());
anyMessage = Any.pack(structBuilder.build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Struct\",\n"
- + " \"value\": {\n"
- + " \"number\": 1.125\n"
- + " }\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Struct\",\n"
+ + " \"value\": {\n"
+ + " \"number\": 1.125\n"
+ + " }\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
Value.Builder valueBuilder = Value.newBuilder();
valueBuilder.setNumberValue(1);
anyMessage = Any.pack(valueBuilder.build());
assertEquals(
"{\n"
- + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n"
- + " \"value\": 1.0\n"
- + "}", printer.print(anyMessage));
+ + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n"
+ + " \"value\": 1.0\n"
+ + "}",
+ printer.print(anyMessage));
assertRoundTripEquals(anyMessage, registry);
}
public void testParserMissingTypeUrl() throws Exception {
try {
Any.Builder builder = Any.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"optionalInt32\": 1234\n"
- + "}", builder);
+ mergeFromJson("{\n" + " \"optionalInt32\": 1234\n" + "}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
@@ -976,9 +976,10 @@ public class JsonFormatTest extends TestCase {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
- + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
- + " \"optionalInt32\": 12345\n"
- + "}", builder);
+ + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ + " \"optionalInt32\": 12345\n"
+ + "}",
+ builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
@@ -988,10 +989,7 @@ public class JsonFormatTest extends TestCase {
public void testParserRejectTrailingComma() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"optionalInt32\": 12345,\n"
- + "}", builder);
+ mergeFromJson("{\n" + " \"optionalInt32\": 12345,\n" + "}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
@@ -1022,10 +1020,7 @@ public class JsonFormatTest extends TestCase {
public void testParserRejectInvalidEnumValue() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
- mergeFromJson(
- "{\n"
- + " \"optionalNestedEnum\": \"XXX\"\n"
- + "}", builder);
+ mergeFromJson("{\n" + " \"optionalNestedEnum\": \"XXX\"\n" + "}", builder);
fail("Exception is expected.");
} catch (InvalidProtocolBufferException e) {
// Expected.
diff --git a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
index 4c31b2b3..a41528ec 100644
--- a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java
@@ -84,6 +84,7 @@ public class TimeUtilTest extends TestCase {
private class ParseTimestampThread extends Thread {
private final String[] strings;
private final Timestamp[] values;
+
public ParseTimestampThread(String[] strings, Timestamp[] values) {
this.strings = strings;
this.values = values;
@@ -102,8 +103,8 @@ public class TimeUtilTest extends TestCase {
}
if (result.getSeconds() != values[index].getSeconds()
|| result.getNanos() != values[index].getNanos()) {
- errorMessage = "Actual result: " + result.toString() + ", expected: "
- + values[index].toString();
+ errorMessage =
+ "Actual result: " + result.toString() + ", expected: " + values[index].toString();
break;
}
index = (index + 1) % strings.length;
@@ -112,26 +113,26 @@ public class TimeUtilTest extends TestCase {
}
public void testTimestampConcurrentParsing() throws Exception {
- String[] timestampStrings = new String[]{
- "0001-01-01T00:00:00Z",
- "9999-12-31T23:59:59.999999999Z",
- "1970-01-01T00:00:00Z",
- "1969-12-31T23:59:59.999Z",
- };
+ String[] timestampStrings =
+ new String[] {
+ "0001-01-01T00:00:00Z",
+ "9999-12-31T23:59:59.999999999Z",
+ "1970-01-01T00:00:00Z",
+ "1969-12-31T23:59:59.999Z",
+ };
Timestamp[] timestampValues = new Timestamp[timestampStrings.length];
for (int i = 0; i < timestampStrings.length; i++) {
timestampValues[i] = TimeUtil.parseTimestamp(timestampStrings[i]);
}
final int THREAD_COUNT = 16;
- final int RUNNING_TIME = 5000; // in milliseconds.
+ final int RUNNING_TIME = 5000; // in milliseconds.
final List<Thread> threads = new ArrayList<Thread>();
stopParsingThreads = false;
errorMessage = "";
for (int i = 0; i < THREAD_COUNT; i++) {
- Thread thread = new ParseTimestampThread(
- timestampStrings, timestampValues);
+ Thread thread = new ParseTimestampThread(timestampStrings, timestampValues);
thread.start();
threads.add(thread);
}
@@ -146,8 +147,8 @@ public class TimeUtilTest extends TestCase {
public void testTimetampInvalidFormat() throws Exception {
try {
// Value too small.
- Timestamp value = Timestamp.newBuilder()
- .setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build();
+ Timestamp value =
+ Timestamp.newBuilder().setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
@@ -156,14 +157,14 @@ public class TimeUtilTest extends TestCase {
try {
// Value too large.
- Timestamp value = Timestamp.newBuilder()
- .setSeconds(TimeUtil.TIMESTAMP_SECONDS_MAX + 1).build();
+ Timestamp value =
+ Timestamp.newBuilder().setSeconds(TimeUtil.TIMESTAMP_SECONDS_MAX + 1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
-
+
try {
// Invalid nanos value.
Timestamp value = Timestamp.newBuilder().setNanos(-1).build();
@@ -279,8 +280,7 @@ public class TimeUtilTest extends TestCase {
public void testDurationInvalidFormat() throws Exception {
try {
// Value too small.
- Duration value = Duration.newBuilder()
- .setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build();
+ Duration value = Duration.newBuilder().setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
@@ -289,18 +289,16 @@ public class TimeUtilTest extends TestCase {
try {
// Value too large.
- Duration value = Duration.newBuilder()
- .setSeconds(TimeUtil.DURATION_SECONDS_MAX + 1).build();
+ Duration value = Duration.newBuilder().setSeconds(TimeUtil.DURATION_SECONDS_MAX + 1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
-
+
try {
// Invalid nanos value.
- Duration value = Duration.newBuilder().setSeconds(1).setNanos(-1)
- .build();
+ Duration value = Duration.newBuilder().setSeconds(1).setNanos(-1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
@@ -309,8 +307,7 @@ public class TimeUtilTest extends TestCase {
try {
// Invalid nanos value.
- Duration value = Duration.newBuilder().setSeconds(-1).setNanos(1)
- .build();
+ Duration value = Duration.newBuilder().setSeconds(-1).setNanos(1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
@@ -367,8 +364,7 @@ public class TimeUtilTest extends TestCase {
}
public void testTimestampConversion() throws Exception {
- Timestamp timestamp =
- TimeUtil.parseTimestamp("1970-01-01T00:00:01.111111111Z");
+ Timestamp timestamp = TimeUtil.parseTimestamp("1970-01-01T00:00:01.111111111Z");
assertEquals(1111111111, TimeUtil.toNanos(timestamp));
assertEquals(1111111, TimeUtil.toMicros(timestamp));
assertEquals(1111, TimeUtil.toMillis(timestamp));
@@ -378,7 +374,7 @@ public class TimeUtilTest extends TestCase {
assertEquals("1970-01-01T00:00:01.111111Z", TimeUtil.toString(timestamp));
timestamp = TimeUtil.createTimestampFromMillis(1111);
assertEquals("1970-01-01T00:00:01.111Z", TimeUtil.toString(timestamp));
-
+
timestamp = TimeUtil.parseTimestamp("1969-12-31T23:59:59.111111111Z");
assertEquals(-888888889, TimeUtil.toNanos(timestamp));
assertEquals(-888889, TimeUtil.toMicros(timestamp));
@@ -402,7 +398,7 @@ public class TimeUtilTest extends TestCase {
assertEquals("1.111111s", TimeUtil.toString(duration));
duration = TimeUtil.createDurationFromMillis(1111);
assertEquals("1.111s", TimeUtil.toString(duration));
-
+
duration = TimeUtil.parseDuration("-1.111111111s");
assertEquals(-1111111111, TimeUtil.toNanos(duration));
assertEquals(-1111111, TimeUtil.toMicros(duration));
@@ -459,29 +455,28 @@ public class TimeUtilTest extends TestCase {
duration = TimeUtil.add(duration, duration);
assertEquals("-2.250s", TimeUtil.toString(duration));
-
+
duration = TimeUtil.subtract(duration, TimeUtil.parseDuration("-1s"));
assertEquals("-1.250s", TimeUtil.toString(duration));
-
+
// Multiplications (with results larger than Long.MAX_VALUE in nanoseconds).
duration = TimeUtil.parseDuration("0.999999999s");
- assertEquals("315575999684.424s",
- TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
+ assertEquals(
+ "315575999684.424s", TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
duration = TimeUtil.parseDuration("-0.999999999s");
- assertEquals("-315575999684.424s",
- TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
- assertEquals("315575999684.424s",
- TimeUtil.toString(TimeUtil.multiply(duration, -315576000000L)));
-
+ assertEquals(
+ "-315575999684.424s", TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L)));
+ assertEquals(
+ "315575999684.424s", TimeUtil.toString(TimeUtil.multiply(duration, -315576000000L)));
+
// Divisions (with values larger than Long.MAX_VALUE in nanoseconds).
Duration d1 = TimeUtil.parseDuration("315576000000s");
Duration d2 = TimeUtil.subtract(d1, TimeUtil.createDurationFromNanos(1));
assertEquals(1, TimeUtil.divide(d1, d2));
assertEquals(0, TimeUtil.divide(d2, d1));
assertEquals("0.000000001s", TimeUtil.toString(TimeUtil.remainder(d1, d2)));
- assertEquals("315575999999.999999999s",
- TimeUtil.toString(TimeUtil.remainder(d2, d1)));
-
+ assertEquals("315575999999.999999999s", TimeUtil.toString(TimeUtil.remainder(d2, d1)));
+
// Divisions involving negative values.
//
// (-5) / 2 = -2, remainder = -1
diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
index 686edc42..4bf223f2 100644
--- a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
+++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
@@ -124,6 +124,7 @@ message TestOneof {
oneof oneof_field {
int32 oneof_int32 = 1;
TestAllTypes.NestedMessage oneof_nested_message = 2;
+ google.protobuf.NullValue oneof_null_value = 3;
}
}