Merge Java util package to github.

This commit is contained in:
Feng Xiao 2015-08-25 14:32:09 -07:00
parent 839b180dba
commit fbb3ef28c9
10 changed files with 4736 additions and 0 deletions

202
java/util/pom.xml Normal file
View File

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.google</groupId>
<artifactId>google</artifactId>
<version>1</version>
</parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.0.0-alpha-4-pre</version>
<packaging>bundle</packaging>
<name>Protocol Buffer Java API</name>
<description>
Protocol Buffers are a way of encoding structured data in an efficient yet
extensible format.
</description>
<inceptionYear>2008</inceptionYear>
<url>https://developers.google.com/protocol-buffers/</url>
<licenses>
<license>
<name>New BSD license</name>
<url>http://www.opensource.org/licenses/bsd-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>https://github.com/google/protobuf</url>
<connection>
scm:git:https://github.com/google/protobuf.git
</connection>
</scm>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.0.0-alpha-4-pre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>2.2.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
<include>../src/main/java/com/google/protobuf/TestUtil.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>generate-test-sources</id>
<phase>generate-test-sources</phase>
<configuration>
<tasks>
<mkdir dir="target/generated-test-sources" />
<exec executable="../../src/protoc">
<arg value="--java_out=target/generated-test-sources" />
<arg value="--proto_path=../../src" />
<arg value="--proto_path=src/test/java" />
<arg value="../../src/google/protobuf/unittest.proto" />
<arg value="../../src/google/protobuf/unittest_import.proto" />
<arg value="../../src/google/protobuf/unittest_import_public.proto" />
<arg value="src/test/java/com/google/protobuf/util/json_test.proto" />
</exec>
</tasks>
<testSourceRoot>target/generated-test-sources</testSourceRoot>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL>
<Bundle-SymbolicName>com.google.protobuf.util</Bundle-SymbolicName>
<Export-Package>com.google.protobuf.util;version=3.0.0-alpha-3</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<distributionManagement>
<snapshotRepository>
<id>sonatype-nexus-staging</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>sonatype-nexus-staging</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.3</version>
<extensions>true</extensions>
<configuration>
<serverId>sonatype-nexus-staging</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>false</autoReleaseAfterClose>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,259 @@
// 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.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.FieldMask;
import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Logger;
/**
* A tree representation of a FieldMask. Each leaf node in this tree represent
* a field path in the FieldMask.
*
* <p>For example, FieldMask "foo.bar,foo.baz,bar.baz" as a tree will be:
* <pre>
* [root] -+- foo -+- bar
* | |
* | +- baz
* |
* +- bar --- baz
* </pre>
*
* <p>By representing FieldMasks with this tree structure we can easily convert
* a FieldMask to a canonical form, merge two FieldMasks, calculate the
* intersection to two FieldMasks and traverse all fields specified by the
* FieldMask in a message tree.
*/
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 final Node root = new Node();
/** Creates an empty FieldMaskTree. */
public FieldMaskTree() {}
/** Creates a FieldMaskTree for a given FieldMask. */
public FieldMaskTree(FieldMask mask) {
mergeFromFieldMask(mask);
}
@Override
public String toString() {
return FieldMaskUtil.toString(toFieldMask());
}
/**
* Adds a field path to the tree. In a FieldMask, every field path matches the
* specified field as well as all its sub-fields. For example, a field path
* "foo.bar" matches field "foo.bar" and also "foo.bar.baz", etc. When adding
* a field path to the tree, redundant sub-paths will be removed. That is,
* after adding "foo.bar" to the tree, "foo.bar.baz" will be removed if it
* exists, which will turn the tree node for "foo.bar" to a leaf node.
* 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) {
String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
if (parts.length == 0) {
return this;
}
Node node = root;
boolean createNewBranch = false;
// Find the matching node in the tree.
for (String part : parts) {
// Check whether the path matches an existing leaf node.
if (!createNewBranch && node != root && node.children.isEmpty()) {
// The path to add is a sub-path of an existing leaf node.
return this;
}
if (node.children.containsKey(part)) {
node = node.children.get(part);
} else {
createNewBranch = true;
Node tmp = new Node();
node.children.put(part, tmp);
node = tmp;
}
}
// Turn the matching node into a leaf node (i.e., remove sub-paths).
node.children.clear();
return this;
}
/**
* Merges all field paths in a FieldMask into this tree.
*/
public FieldMaskTree mergeFromFieldMask(FieldMask mask) {
for (String path : mask.getPathsList()) {
addFieldPath(path);
}
return this;
}
/** Converts this tree to a FieldMask. */
public FieldMask toFieldMask() {
if (root.children.isEmpty()) {
return FieldMask.getDefaultInstance();
}
List<String> paths = new ArrayList<String>();
getFieldPaths(root, "", paths);
return FieldMask.newBuilder().addAllPaths(paths).build();
}
/** 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);
return;
}
for (Entry<String, Node> entry : node.children.entrySet()) {
String childPath = path.isEmpty()
? entry.getKey() : path + "." + entry.getKey();
getFieldPaths(entry.getValue(), childPath, paths);
}
}
/**
* Adds the intersection of this tree with the given {@code path} to
* {@code output}.
*/
public void intersectFieldPath(String path, FieldMaskTree output) {
if (root.children.isEmpty()) {
return;
}
String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX);
if (parts.length == 0) {
return;
}
Node node = root;
for (String part : parts) {
if (node != root && node.children.isEmpty()) {
// The given path is a sub-path of an existing leaf node in the tree.
output.addFieldPath(path);
return;
}
if (node.children.containsKey(part)) {
node = node.children.get(part);
} else {
return;
}
}
// We found a matching node for the path. All leaf children of this matching
// node is in the intersection.
List<String> paths = new ArrayList<String>();
getFieldPaths(node, path, paths);
for (String value : paths) {
output.addFieldPath(value);
}
}
/**
* Merges all fields specified by this FieldMaskTree from {@code source} to
* {@code destination}.
*/
public void merge(Message source, Message.Builder destination,
FieldMaskUtil.MergeOptions options) {
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
throw new IllegalArgumentException(
"Cannot merge messages of different types.");
}
if (root.children.isEmpty()) {
return;
}
merge(root, "", source, destination, options);
}
/** Merges all fields specified by a sub-tree from {@code source} to
* {@code destination}.
*/
private void merge(Node node, String path, Message source,
Message.Builder destination, FieldMaskUtil.MergeOptions options) {
assert source.getDescriptorForType() == destination.getDescriptorForType();
Descriptor descriptor = source.getDescriptorForType();
for (Entry<String, Node> entry : node.children.entrySet()) {
FieldDescriptor field =
descriptor.findFieldByName(entry.getKey());
if (field == null) {
logger.warning("Cannot find field \"" + entry.getKey()
+ "\" in message type " + descriptor.getFullName());
continue;
}
if (!entry.getValue().children.isEmpty()) {
if (field.isRepeated()
|| field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
logger.warning("Field \"" + field.getFullName() + "\" is not a "
+ "singluar message field and cannot have sub-fields.");
continue;
}
String childPath = path.isEmpty()
? entry.getKey() : path + "." + entry.getKey();
merge(entry.getValue(), childPath, (Message) source.getField(field),
destination.getFieldBuilder(field), options);
continue;
}
if (field.isRepeated()) {
if (options.replaceRepeatedFields()) {
destination.setField(field, source.getField(field));
} else {
for (Object element : (List) source.getField(field)) {
destination.addRepeatedField(field, element);
}
}
} else {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (options.replaceMessageFields()) {
destination.setField(field, source.getField(field));
} else {
destination.getFieldBuilder(field).mergeFrom(
(Message) source.getField(field));
}
} else {
destination.setField(field, source.getField(field));
}
}
}
}
}

View File

@ -0,0 +1,222 @@
// 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.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.FieldMask;
import com.google.protobuf.Internal;
import com.google.protobuf.Message;
import java.util.Arrays;
import java.util.List;
/**
* Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
*/
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() {}
/**
* Converts a FieldMask to a string.
*/
public static String toString(FieldMask fieldMask) {
StringBuilder result = new StringBuilder();
boolean first = true;
for (String value : fieldMask.getPathsList()) {
if (value.isEmpty()) {
// Ignore empty paths.
continue;
}
if (first) {
first = false;
} else {
result.append(FIELD_PATH_SEPARATOR);
}
result.append(value);
}
return result.toString();
}
/**
* Parses from a string to a FieldMask.
*/
public static FieldMask fromString(String value) {
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)
throws IllegalArgumentException {
return fromStringList(
type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
}
/**
* Constructs a FieldMask for a list of field paths in a certain type.
*
* @throws IllegalArgumentException if any of the field path is not valid.
*/
public static FieldMask fromStringList(
Class<? extends Message> type, List<String> paths)
throws IllegalArgumentException {
FieldMask.Builder builder = FieldMask.newBuilder();
for (String path : paths) {
if (path.isEmpty()) {
// Ignore empty field paths.
continue;
}
if (type != null && !isValid(type, path)) {
throw new IllegalArgumentException(
path + " is not a valid path for " + type);
}
builder.addPaths(path);
}
return builder.build();
}
/**
* Checks whether a given field path is valid.
*/
public static boolean isValid(Class<? extends Message> type, String path) {
String[] parts = path.split(FIELD_SEPARATOR_REGEX);
if (parts.length == 0) {
return false;
}
Descriptor descriptor =
Internal.getDefaultInstance(type).getDescriptorForType();
for (String name : parts) {
if (descriptor == null) {
return false;
}
FieldDescriptor field = descriptor.findFieldByName(name);
if (field == null) {
return false;
}
if (!field.isRepeated()
&& field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
descriptor = field.getMessageType();
} else {
descriptor = null;
}
}
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
* paths are moved.
*/
public static FieldMask normalize(FieldMask mask) {
return new FieldMaskTree(mask).toFieldMask();
}
/**
* Creates an union of two FieldMasks.
*/
public static FieldMask union(FieldMask mask1, FieldMask mask2) {
return new FieldMaskTree(mask1).mergeFromFieldMask(mask2).toFieldMask();
}
/**
* Calculates the intersection of two FieldMasks.
*/
public static FieldMask intersection(FieldMask mask1, FieldMask mask2) {
FieldMaskTree tree = new FieldMaskTree(mask1);
FieldMaskTree result = new FieldMaskTree();
for (String path : mask2.getPathsList()) {
tree.intersectFieldPath(path, result);
}
return result.toFieldMask();
}
/**
* Options to customize merging behavior.
*/
public static class MergeOptions {
private boolean replaceMessageFields = false;
private boolean replaceRepeatedFields = false;
/**
* Whether to replace message fields (i.e., discard existing content in
* destination message fields) when merging.
* Default behavior is to merge the source message field into the
* destination message field.
*/
public boolean replaceMessageFields() {
return replaceMessageFields;
}
/**
* Whether to replace repeated fields (i.e., discard existing content in
* destination repeated fields) when merging.
* Default behavior is to append elements from source repeated field to the
* destination repeated field.
*/
public boolean replaceRepeatedFields() {
return replaceRepeatedFields;
}
public void setReplaceMessageFields(boolean value) {
replaceMessageFields = value;
}
public void setReplaceRepeatedFields(boolean value) {
replaceRepeatedFields = value;
}
}
/**
* Merges fields specified by a FieldMask from one message to another.
*/
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) {
merge(mask, source, destination, new MergeOptions());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,545 @@
// 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.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
*/
public class TimeUtil {
// Timestamp for "0001-01-01T00:00:00Z"
public static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
// Timestamp for "9999-12-31T23:59:59Z"
public static final long TIMESTAMP_SECONDS_MAX = 253402300799L;
public static final long DURATION_SECONDS_MIN = -315576000000L;
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 SimpleDateFormat timestampFormat =
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() {}
/**
* 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)
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.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();
}
/**
* 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 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.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);
}
}
/**
* 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)
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();
}
/**
* Parse from a string to produce a duration.
*
* @return A Duration parsed from the string.
* @throws ParseException if parsing fails.
*/
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);
}
}
/**
* Create a Timestamp from the number of milliseconds elapsed from the epoch.
*/
public static Timestamp createTimestampFromMillis(long milliseconds) {
return normalizedTimestamp(milliseconds / MILLIS_PER_SECOND,
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
}
/**
* Create a Duration from the number of milliseconds.
*/
public static Duration createDurationFromMillis(long milliseconds) {
return normalizedDuration(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;
}
/**
* 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 Timestamp from the number of microseconds elapsed from the epoch.
*/
public static Timestamp createTimestampFromMicros(long microseconds) {
return normalizedTimestamp(microseconds / MICROS_PER_SECOND,
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
}
/**
* Create a Duration from the number of microseconds.
*/
public static Duration createDurationFromMicros(long microseconds) {
return normalizedDuration(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;
}
/**
* 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 Timestamp from the number of nanoseconds elapsed from the epoch.
*/
public static Timestamp createTimestampFromNanos(long nanoseconds) {
return normalizedTimestamp(nanoseconds / NANOS_PER_SECOND,
(int) (nanoseconds % NANOS_PER_SECOND));
}
/**
* Create a Duration from the number of nanoseconds.
*/
public static Duration createDurationFromNanos(long nanoseconds) {
return normalizedDuration(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();
}
/**
* Convert a Duration to the number of nanoseconds.
*/
public static long toNanos(Duration duration) {
return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos();
}
/**
* Get the current time.
*/
public static Timestamp getCurrentTime() {
return createTimestampFromMillis(System.currentTimeMillis());
}
/**
* Get the epoch.
*/
public static Timestamp getEpoch() {
return Timestamp.getDefaultInstance();
}
/**
* Calculate the difference between two timestamps.
*/
public static Duration distance(Timestamp from, Timestamp to) {
return 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());
}
/**
* 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());
}
// Multiplications and divisions.
public static Duration multiply(Duration duration, double times) {
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.");
}
long seconds = (long) result;
int nanos = (int) ((result - seconds) * 1000000000);
return normalizedDuration(seconds, nanos);
}
public static Duration divide(Duration duration, double value) {
return multiply(duration, 1.0 / value);
}
public static Duration multiply(Duration duration, long times) {
return createDurationFromBigInteger(
toBigInteger(duration).multiply(toBigInteger(times)));
}
public static Duration divide(Duration duration, long times) {
return createDurationFromBigInteger(
toBigInteger(duration).divide(toBigInteger(times)));
}
public static long divide(Duration d1, Duration d2) {
return toBigInteger(d1).divide(toBigInteger(d2)).longValue();
}
public static Duration remainder(Duration d1, Duration 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()));
}
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();
return normalizedDuration(seconds, nanos);
}
private 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;
}
if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) {
throw new IllegalArgumentException("Duration is out of valid range.");
}
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;
}
}

View File

@ -0,0 +1,229 @@
// 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 protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import junit.framework.TestCase;
public class FieldMaskTreeTest extends TestCase {
public void testAddFieldPath() throws Exception {
FieldMaskTree tree = new FieldMaskTree();
assertEquals("", tree.toString());
tree.addFieldPath("");
assertEquals("", tree.toString());
// New branch.
tree.addFieldPath("foo");
assertEquals("foo", tree.toString());
// Redundant path.
tree.addFieldPath("foo");
assertEquals("foo", tree.toString());
// New branch.
tree.addFieldPath("bar.baz");
assertEquals("bar.baz,foo", tree.toString());
// Redundant sub-path.
tree.addFieldPath("foo.bar");
assertEquals("bar.baz,foo", tree.toString());
// New branch from a non-root node.
tree.addFieldPath("bar.quz");
assertEquals("bar.baz,bar.quz,foo", tree.toString());
// A path that matches several existing sub-paths.
tree.addFieldPath("bar");
assertEquals("bar,foo", tree.toString());
}
public void testMergeFromFieldMask() throws Exception {
FieldMaskTree tree = new FieldMaskTree(
FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
assertEquals("bar.baz,bar.quz,foo", tree.toString());
tree.mergeFromFieldMask(
FieldMaskUtil.fromString("foo.bar,bar"));
assertEquals("bar,foo", tree.toString());
}
public void testIntersectFieldPath() throws Exception {
FieldMaskTree tree = new FieldMaskTree(
FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
FieldMaskTree result = new FieldMaskTree();
// Empty path.
tree.intersectFieldPath("", result);
assertEquals("", result.toString());
// Non-exist path.
tree.intersectFieldPath("quz", result);
assertEquals("", result.toString());
// Sub-path of an existing leaf.
tree.intersectFieldPath("foo.bar", result);
assertEquals("foo.bar", result.toString());
// Match an existing leaf node.
tree.intersectFieldPath("foo", result);
assertEquals("foo", result.toString());
// Non-exist path.
tree.intersectFieldPath("bar.foo", result);
assertEquals("foo", result.toString());
// Match a non-leaf node.
tree.intersectFieldPath("bar", result);
assertEquals("bar.baz,bar.quz,foo", result.toString());
}
public void testMerge() throws Exception {
TestAllTypes value = TestAllTypes.newBuilder()
.setOptionalInt32(1234)
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
.addRepeatedInt32(4321)
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
.build();
NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
.setPayload(value)
.setChild(NestedTestAllTypes.newBuilder().setPayload(value))
.build();
// Now we have a message source with the following structure:
// [root] -+- payload -+- optional_int32
// | +- optional_nested_message
// | +- repeated_int32
// | +- repeated_nested_message
// |
// +- child --- payload -+- optional_int32
// +- optional_nested_message
// +- repeated_int32
// +- repeated_nested_message
FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
// Test merging each individual field.
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("payload.optional_int32")
.merge(source, builder, options);
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("payload.optional_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalNestedMessage(
NestedMessage.newBuilder().setBb(5678));
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("payload.repeated_int32")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedInt32(4321);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("payload.repeated_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedNestedMessage(
NestedMessage.newBuilder().setBb(8765));
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("child.payload.optional_int32")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("child.payload.optional_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().setOptionalNestedMessage(
NestedMessage.newBuilder().setBb(5678));
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("child.payload.repeated_int32")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
assertEquals(expected.build(), builder.build());
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message")
.merge(source, builder, options);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().addRepeatedNestedMessage(
NestedMessage.newBuilder().setBb(8765));
assertEquals(expected.build(), builder.build());
// Test merging all fields.
builder = NestedTestAllTypes.newBuilder();
new FieldMaskTree().addFieldPath("child").addFieldPath("payload")
.merge(source, builder, options);
assertEquals(source, builder.build());
// Test repeated options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().addRepeatedInt32(1000);
new FieldMaskTree().addFieldPath("payload.repeated_int32")
.merge(source, builder, options);
// Default behavior is to append repeated fields.
assertEquals(2, builder.getPayload().getRepeatedInt32Count());
assertEquals(1000, builder.getPayload().getRepeatedInt32(0));
assertEquals(4321, builder.getPayload().getRepeatedInt32(1));
// Change to replace repeated fields.
options.setReplaceRepeatedFields(true);
new FieldMaskTree().addFieldPath("payload.repeated_int32")
.merge(source, builder, options);
assertEquals(1, builder.getPayload().getRepeatedInt32Count());
assertEquals(4321, builder.getPayload().getRepeatedInt32(0));
// Test message options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
new FieldMaskTree().addFieldPath("payload")
.merge(source, builder, options);
// Default behavior is to merge message fields.
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(2000, builder.getPayload().getOptionalUint32());
// Change to replace message fields.
options.setReplaceMessageFields(true);
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
new FieldMaskTree().addFieldPath("payload")
.merge(source, builder, options);
assertEquals(1234, builder.getPayload().getOptionalInt32());
assertEquals(0, builder.getPayload().getOptionalUint32());
}
}

View File

@ -0,0 +1,135 @@
// 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.FieldMask;
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes;
import junit.framework.TestCase;
/** Unit tests for {@link FieldMaskUtil}. */
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, "payload.optional_nested_message.bb"));
// Repeated fields cannot have sub-paths.
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"));
}
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();
assertEquals("foo,bar", FieldMaskUtil.toString(mask));
}
public void testFromString() throws Exception {
FieldMask mask = FieldMaskUtil.fromString("");
assertEquals(0, mask.getPathsCount());
mask = FieldMaskUtil.fromString("foo");
assertEquals(1, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
mask = FieldMaskUtil.fromString("foo,bar.baz");
assertEquals(2, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
assertEquals("bar.baz", mask.getPaths(1));
// Empty field paths are ignore.
mask = FieldMaskUtil.fromString(",foo,,bar,");
assertEquals(2, mask.getPathsCount());
assertEquals("foo", mask.getPaths(0));
assertEquals("bar", mask.getPaths(1));
// Check whether the field paths are valid if a class parameter is provided.
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload");
try {
mask = FieldMaskUtil.fromString(
NestedTestAllTypes.class, "payload,nonexist");
fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
}
public void testUnion() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios.
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar");
FieldMask result = FieldMaskUtil.union(mask1, mask2);
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.
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar");
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.Builder builder = NestedTestAllTypes.newBuilder();
FieldMaskUtil.merge(FieldMaskUtil.fromString("payload"), source, builder);
assertEquals(1234, builder.getPayload().getOptionalInt32());
}
}

View File

@ -0,0 +1,976 @@
// 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.Any;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import com.google.protobuf.BytesValue;
import com.google.protobuf.DoubleValue;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.ListValue;
import com.google.protobuf.Message;
import com.google.protobuf.StringValue;
import com.google.protobuf.Struct;
import com.google.protobuf.UInt32Value;
import com.google.protobuf.UInt64Value;
import com.google.protobuf.Value;
import com.google.protobuf.util.JsonFormat.TypeRegistry;
import com.google.protobuf.util.JsonTestProto.TestAllTypes;
import com.google.protobuf.util.JsonTestProto.TestAllTypes.NestedEnum;
import com.google.protobuf.util.JsonTestProto.TestAllTypes.NestedMessage;
import com.google.protobuf.util.JsonTestProto.TestAny;
import com.google.protobuf.util.JsonTestProto.TestDuration;
import com.google.protobuf.util.JsonTestProto.TestFieldMask;
import com.google.protobuf.util.JsonTestProto.TestMap;
import com.google.protobuf.util.JsonTestProto.TestStruct;
import com.google.protobuf.util.JsonTestProto.TestTimestamp;
import com.google.protobuf.util.JsonTestProto.TestWrappers;
import junit.framework.TestCase;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
public class JsonFormatTest extends TestCase {
private void setAllFields(TestAllTypes.Builder builder) {
builder.setOptionalInt32(1234);
builder.setOptionalInt64(1234567890123456789L);
builder.setOptionalUint32(5678);
builder.setOptionalUint64(2345678901234567890L);
builder.setOptionalSint32(9012);
builder.setOptionalSint64(3456789012345678901L);
builder.setOptionalFixed32(3456);
builder.setOptionalFixed64(4567890123456789012L);
builder.setOptionalSfixed32(7890);
builder.setOptionalSfixed64(5678901234567890123L);
builder.setOptionalFloat(1.5f);
builder.setOptionalDouble(1.25);
builder.setOptionalBool(true);
builder.setOptionalString("Hello world!");
builder.setOptionalBytes(ByteString.copyFrom(new byte[]{0, 1, 2}));
builder.setOptionalNestedEnum(NestedEnum.BAR);
builder.getOptionalNestedMessageBuilder().setValue(100);
builder.addRepeatedInt32(1234);
builder.addRepeatedInt64(1234567890123456789L);
builder.addRepeatedUint32(5678);
builder.addRepeatedUint64(2345678901234567890L);
builder.addRepeatedSint32(9012);
builder.addRepeatedSint64(3456789012345678901L);
builder.addRepeatedFixed32(3456);
builder.addRepeatedFixed64(4567890123456789012L);
builder.addRepeatedSfixed32(7890);
builder.addRepeatedSfixed64(5678901234567890123L);
builder.addRepeatedFloat(1.5f);
builder.addRepeatedDouble(1.25);
builder.addRepeatedBool(true);
builder.addRepeatedString("Hello world!");
builder.addRepeatedBytes(ByteString.copyFrom(new byte[]{0, 1, 2}));
builder.addRepeatedNestedEnum(NestedEnum.BAR);
builder.addRepeatedNestedMessageBuilder().setValue(100);
builder.addRepeatedInt32(234);
builder.addRepeatedInt64(234567890123456789L);
builder.addRepeatedUint32(678);
builder.addRepeatedUint64(345678901234567890L);
builder.addRepeatedSint32(012);
builder.addRepeatedSint64(456789012345678901L);
builder.addRepeatedFixed32(456);
builder.addRepeatedFixed64(567890123456789012L);
builder.addRepeatedSfixed32(890);
builder.addRepeatedSfixed64(678901234567890123L);
builder.addRepeatedFloat(11.5f);
builder.addRepeatedDouble(11.25);
builder.addRepeatedBool(true);
builder.addRepeatedString("ello world!");
builder.addRepeatedBytes(ByteString.copyFrom(new byte[]{1, 2}));
builder.addRepeatedNestedEnum(NestedEnum.BAZ);
builder.addRepeatedNestedMessageBuilder().setValue(200);
}
private void assertRoundTripEquals(Message message) throws Exception {
assertRoundTripEquals(message, TypeRegistry.getEmptyTypeRegistry());
}
private void assertRoundTripEquals(Message message, TypeRegistry registry) throws Exception {
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry);
Message.Builder builder = message.newBuilderForType();
parser.merge(printer.print(message), builder);
Message parsedMessage = builder.build();
assertEquals(message.toString(), parsedMessage.toString());
}
private String toJsonString(Message message) throws IOException {
return JsonFormat.printer().print(message);
}
private void mergeFromJson(String json, Message.Builder builder) throws IOException {
JsonFormat.parser().merge(json, builder);
}
public void testAllFields() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
setAllFields(builder);
TestAllTypes message = builder.build();
assertEquals(
"{\n"
+ " \"optionalInt32\": 1234,\n"
+ " \"optionalInt64\": \"1234567890123456789\",\n"
+ " \"optionalUint32\": 5678,\n"
+ " \"optionalUint64\": \"2345678901234567890\",\n"
+ " \"optionalSint32\": 9012,\n"
+ " \"optionalSint64\": \"3456789012345678901\",\n"
+ " \"optionalFixed32\": 3456,\n"
+ " \"optionalFixed64\": \"4567890123456789012\",\n"
+ " \"optionalSfixed32\": 7890,\n"
+ " \"optionalSfixed64\": \"5678901234567890123\",\n"
+ " \"optionalFloat\": 1.5,\n"
+ " \"optionalDouble\": 1.25,\n"
+ " \"optionalBool\": true,\n"
+ " \"optionalString\": \"Hello world!\",\n"
+ " \"optionalBytes\": \"AAEC\",\n"
+ " \"optionalNestedMessage\": {\n"
+ " \"value\": 100\n"
+ " },\n"
+ " \"optionalNestedEnum\": \"BAR\",\n"
+ " \"repeatedInt32\": [1234, 234],\n"
+ " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n"
+ " \"repeatedUint32\": [5678, 678],\n"
+ " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n"
+ " \"repeatedSint32\": [9012, 10],\n"
+ " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n"
+ " \"repeatedFixed32\": [3456, 456],\n"
+ " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n"
+ " \"repeatedSfixed32\": [7890, 890],\n"
+ " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n"
+ " \"repeatedFloat\": [1.5, 11.5],\n"
+ " \"repeatedDouble\": [1.25, 11.25],\n"
+ " \"repeatedBool\": [true, true],\n"
+ " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n"
+ " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n"
+ " \"repeatedNestedMessage\": [{\n"
+ " \"value\": 100\n"
+ " }, {\n"
+ " \"value\": 200\n"
+ " }],\n"
+ " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n"
+ "}",
toJsonString(message));
assertRoundTripEquals(message);
}
public void testUnknownEnumValues() throws Exception {
// Unknown enum values will be dropped.
// TODO(xiaofeng): We may want to revisit this (whether we should omit
// unknown enum values).
TestAllTypes message = TestAllTypes.newBuilder()
.setOptionalNestedEnumValue(12345)
.addRepeatedNestedEnumValue(12345)
.addRepeatedNestedEnumValue(0)
.build();
assertEquals(
"{\n"
+ " \"repeatedNestedEnum\": [\"FOO\"]\n"
+ "}", toJsonString(message));
TestMap.Builder mapBuilder = TestMap.newBuilder();
mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0);
mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345);
TestMap mapMessage = mapBuilder.build();
assertEquals(
"{\n"
+ " \"int32ToEnumMap\": {\n"
+ " \"1\": \"FOO\"\n"
+ " }\n"
+ "}", toJsonString(mapMessage));
}
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();
assertEquals(
"{\n"
+ " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n"
+ " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
public void testParserAcceptStringForNumbericField() throws Exception {
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);
TestAllTypes message = builder.build();
assertEquals(1234, message.getOptionalInt32());
assertEquals(5678, message.getOptionalUint32());
assertEquals(9012, message.getOptionalSint32());
assertEquals(3456, message.getOptionalFixed32());
assertEquals(7890, message.getOptionalSfixed32());
assertEquals(1.5f, message.getOptionalFloat());
assertEquals(1.25, message.getOptionalDouble());
assertEquals(true, message.getOptionalBool());
}
private void assertRejects(String name, String value) {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
// Numeric form is rejected.
mergeFromJson("{\"" + name + "\":" + value + "}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
}
try {
// String form is also rejected.
mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
}
}
private void assertAccepts(String name, String value) throws IOException {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
// Both numeric form and string form are accepted.
mergeFromJson("{\"" + name + "\":" + value + "}", builder);
mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder);
}
public void testParserRejectOutOfRangeNumericValues() throws Exception {
assertAccepts("optionalInt32", String.valueOf(Integer.MAX_VALUE));
assertAccepts("optionalInt32", String.valueOf(Integer.MIN_VALUE));
assertRejects("optionalInt32", String.valueOf(Integer.MAX_VALUE + 1L));
assertRejects("optionalInt32", String.valueOf(Integer.MIN_VALUE - 1L));
assertAccepts("optionalUint32", String.valueOf(Integer.MAX_VALUE + 1L));
assertRejects("optionalUint32", "123456789012345");
assertRejects("optionalUint32", "-1");
BigInteger one = new BigInteger("1");
BigInteger maxLong = new BigInteger(String.valueOf(Long.MAX_VALUE));
BigInteger minLong = new BigInteger(String.valueOf(Long.MIN_VALUE));
assertAccepts("optionalInt64", maxLong.toString());
assertAccepts("optionalInt64", minLong.toString());
assertRejects("optionalInt64", maxLong.add(one).toString());
assertRejects("optionalInt64", minLong.subtract(one).toString());
assertAccepts("optionalUint64", maxLong.add(one).toString());
assertRejects("optionalUint64", "1234567890123456789012345");
assertRejects("optionalUint64", "-1");
assertAccepts("optionalBool", "true");
assertRejects("optionalBool", "1");
assertRejects("optionalBool", "0");
assertAccepts("optionalFloat", String.valueOf(Float.MAX_VALUE));
assertAccepts("optionalFloat", String.valueOf(-Float.MAX_VALUE));
assertRejects("optionalFloat", String.valueOf(Double.MAX_VALUE));
assertRejects("optionalFloat", String.valueOf(-Double.MAX_VALUE));
BigDecimal moreThanOne = new BigDecimal("1.000001");
BigDecimal maxDouble = new BigDecimal(Double.MAX_VALUE);
BigDecimal minDouble = new BigDecimal(-Double.MAX_VALUE);
assertAccepts("optionalDouble", maxDouble.toString());
assertAccepts("optionalDouble", minDouble.toString());
assertRejects("optionalDouble", maxDouble.multiply(moreThanOne).toString());
assertRejects("optionalDouble", minDouble.multiply(moreThanOne).toString());
}
public void testParserAcceptNull() throws Exception {
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);
TestAllTypes message = builder.build();
assertEquals(TestAllTypes.getDefaultInstance(), message);
// Repeated field elements can also be null.
builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
+ " \"repeatedInt32\": [null, null],\n"
+ " \"repeatedInt64\": [null, null],\n"
+ " \"repeatedUint32\": [null, null],\n"
+ " \"repeatedUint64\": [null, null],\n"
+ " \"repeatedSint32\": [null, null],\n"
+ " \"repeatedSint64\": [null, null],\n"
+ " \"repeatedFixed32\": [null, null],\n"
+ " \"repeatedFixed64\": [null, null],\n"
+ " \"repeatedSfixed32\": [null, null],\n"
+ " \"repeatedSfixed64\": [null, null],\n"
+ " \"repeatedFloat\": [null, null],\n"
+ " \"repeatedDouble\": [null, null],\n"
+ " \"repeatedBool\": [null, null],\n"
+ " \"repeatedString\": [null, null],\n"
+ " \"repeatedBytes\": [null, null],\n"
+ " \"repeatedNestedMessage\": [null, null],\n"
+ " \"repeatedNestedEnum\": [null, null]\n"
+ "}", builder);
message = builder.build();
// "null" elements will be parsed to default values.
assertEquals(2, message.getRepeatedInt32Count());
assertEquals(0, message.getRepeatedInt32(0));
assertEquals(0, message.getRepeatedInt32(1));
assertEquals(2, message.getRepeatedInt32Count());
assertEquals(0, message.getRepeatedInt32(0));
assertEquals(0, message.getRepeatedInt32(1));
assertEquals(2, message.getRepeatedInt64Count());
assertEquals(0, message.getRepeatedInt64(0));
assertEquals(0, message.getRepeatedInt64(1));
assertEquals(2, message.getRepeatedUint32Count());
assertEquals(0, message.getRepeatedUint32(0));
assertEquals(0, message.getRepeatedUint32(1));
assertEquals(2, message.getRepeatedUint64Count());
assertEquals(0, message.getRepeatedUint64(0));
assertEquals(0, message.getRepeatedUint64(1));
assertEquals(2, message.getRepeatedSint32Count());
assertEquals(0, message.getRepeatedSint32(0));
assertEquals(0, message.getRepeatedSint32(1));
assertEquals(2, message.getRepeatedSint64Count());
assertEquals(0, message.getRepeatedSint64(0));
assertEquals(0, message.getRepeatedSint64(1));
assertEquals(2, message.getRepeatedFixed32Count());
assertEquals(0, message.getRepeatedFixed32(0));
assertEquals(0, message.getRepeatedFixed32(1));
assertEquals(2, message.getRepeatedFixed64Count());
assertEquals(0, message.getRepeatedFixed64(0));
assertEquals(0, message.getRepeatedFixed64(1));
assertEquals(2, message.getRepeatedSfixed32Count());
assertEquals(0, message.getRepeatedSfixed32(0));
assertEquals(0, message.getRepeatedSfixed32(1));
assertEquals(2, message.getRepeatedSfixed64Count());
assertEquals(0, message.getRepeatedSfixed64(0));
assertEquals(0, message.getRepeatedSfixed64(1));
assertEquals(2, message.getRepeatedFloatCount());
assertEquals(0f, message.getRepeatedFloat(0));
assertEquals(0f, message.getRepeatedFloat(1));
assertEquals(2, message.getRepeatedDoubleCount());
assertEquals(0.0, message.getRepeatedDouble(0));
assertEquals(0.0, message.getRepeatedDouble(1));
assertEquals(2, message.getRepeatedBoolCount());
assertFalse(message.getRepeatedBool(0));
assertFalse(message.getRepeatedBool(1));
assertEquals(2, message.getRepeatedStringCount());
assertTrue(message.getRepeatedString(0).isEmpty());
assertTrue(message.getRepeatedString(1).isEmpty());
assertEquals(2, message.getRepeatedBytesCount());
assertTrue(message.getRepeatedBytes(0).isEmpty());
assertTrue(message.getRepeatedBytes(1).isEmpty());
assertEquals(2, message.getRepeatedNestedMessageCount());
assertEquals(NestedMessage.getDefaultInstance(), message.getRepeatedNestedMessage(0));
assertEquals(NestedMessage.getDefaultInstance(), message.getRepeatedNestedMessage(1));
assertEquals(2, message.getRepeatedNestedEnumCount());
assertEquals(0, message.getRepeatedNestedEnumValue(0));
assertEquals(0, message.getRepeatedNestedEnumValue(1));
}
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);
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));
assertRoundTripEquals(message);
// Test multiple entries.
builder = TestMap.newBuilder();
builder.getMutableInt32ToInt32Map().put(1, 2);
builder.getMutableInt32ToInt32Map().put(3, 4);
message = builder.build();
assertEquals(
"{\n"
+ " \"int32ToInt32Map\": {\n"
+ " \"1\": 2,\n"
+ " \"3\": 4\n"
+ " }\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
public void testMapNullValueIsDefault() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
"{\n"
+ " \"int32ToInt32Map\": {\"1\": null},\n"
+ " \"int32ToMessageMap\": {\"2\": null}\n"
+ "}", builder);
TestMap message = builder.build();
assertTrue(message.getInt32ToInt32Map().containsKey(1));
assertEquals(0, message.getInt32ToInt32Map().get(1).intValue());
assertTrue(message.getInt32ToMessageMap().containsKey(2));
assertEquals(0, message.getInt32ToMessageMap().get(2).getValue());
}
public void testParserAcceptNonQuotedObjectKey() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
mergeFromJson(
"{\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());
}
public void testWrappers() throws Exception {
TestWrappers.Builder builder = TestWrappers.newBuilder();
builder.getBoolValueBuilder().setValue(false);
builder.getInt32ValueBuilder().setValue(0);
builder.getInt64ValueBuilder().setValue(0);
builder.getUint32ValueBuilder().setValue(0);
builder.getUint64ValueBuilder().setValue(0);
builder.getFloatValueBuilder().setValue(0.0f);
builder.getDoubleValueBuilder().setValue(0.0);
builder.getStringValueBuilder().setValue("");
builder.getBytesValueBuilder().setValue(ByteString.EMPTY);
TestWrappers message = builder.build();
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));
assertRoundTripEquals(message);
builder = TestWrappers.newBuilder();
builder.getBoolValueBuilder().setValue(true);
builder.getInt32ValueBuilder().setValue(1);
builder.getInt64ValueBuilder().setValue(2);
builder.getUint32ValueBuilder().setValue(3);
builder.getUint64ValueBuilder().setValue(4);
builder.getFloatValueBuilder().setValue(5.0f);
builder.getDoubleValueBuilder().setValue(6.0);
builder.getStringValueBuilder().setValue("7");
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));
assertRoundTripEquals(message);
}
public void testTimestamp() throws Exception {
TestTimestamp message = TestTimestamp.newBuilder()
.setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z"))
.build();
assertEquals(
"{\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();
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();
assertEquals(
"{\n"
+ " \"fieldMaskValue\": \"foo.bar,baz\"\n"
+ "}", toJsonString(message));
assertRoundTripEquals(message);
}
public void testStruct() throws Exception {
// 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());
Struct.Builder subStructBuilder = Struct.newBuilder();
subStructBuilder.getMutableFields().put(
"number_value", Value.newBuilder().setNumberValue(1234).build());
structBuilder.getMutableFields().put(
"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(
"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));
assertRoundTripEquals(message);
}
public void testAnyFields() throws Exception {
TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
// A TypeRegistry must be provided in order to convert Any types.
try {
toJsonString(message);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
}
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
assertRoundTripEquals(anyMessage, registry);
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));
assertRoundTripEquals(anyMessage, registry);
// 3. Timestamp in Any.
anyMessage = Any.pack(TimeUtil.parseTimestamp("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));
assertRoundTripEquals(anyMessage, registry);
// 4. Duration in Any
anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s"));
assertEquals(
"{\n"
+ " \"@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));
assertRoundTripEquals(anyMessage, registry);
// 6. Struct in Any
Struct.Builder structBuilder = Struct.newBuilder();
structBuilder.getMutableFields().put(
"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));
assertRoundTripEquals(anyMessage, registry);
}
public void testParserMissingTypeUrl() throws Exception {
try {
Any.Builder builder = Any.newBuilder();
mergeFromJson(
"{\n"
+ " \"optionalInt32\": 1234\n"
+ "}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
}
}
public void testParserUnexpectedTypeUrl() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
+ " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+ " \"optionalInt32\": 12345\n"
+ "}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
}
}
public void testParserRejectTrailingComma() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
+ " \"optionalInt32\": 12345,\n"
+ "}", builder);
fail("Exception is expected.");
} catch (IOException e) {
// Expected.
}
// TODO(xiaofeng): GSON allows trailing comma in arrays even after I set
// the JsonReader to non-lenient mode. If we want to enforce strict JSON
// compliance, we might want to switch to a different JSON parser or
// implement one by ourselves.
// try {
// TestAllTypes.Builder builder = TestAllTypes.newBuilder();
// JsonFormat.merge(
// "{\n"
// + " \"repeatedInt32\": [12345,]\n"
// + "}", builder);
// fail("Exception is expected.");
// } catch (IOException e) {
// // Expected.
// }
}
public void testParserRejectInvalidBase64() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
+ " \"optionalBytes\": \"!@#$\"\n"
+ "}", builder);
fail("Exception is expected.");
} catch (InvalidProtocolBufferException e) {
// Expected.
}
}
public void testParserRejectInvalidEnumValue() throws Exception {
try {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
mergeFromJson(
"{\n"
+ " \"optionalNestedEnum\": \"XXX\"\n"
+ "}", builder);
fail("Exception is expected.");
} catch (InvalidProtocolBufferException e) {
// Expected.
}
}
}

View File

@ -0,0 +1,439 @@
// 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 junit.framework.TestCase;
import org.junit.Assert;
import java.text.ParseException;
/** Unit tests for {@link TimeUtil}. */
public class TimeUtilTest extends TestCase {
public void testTimestampStringFormat() throws Exception {
Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z");
Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z");
assertEquals(TimeUtil.TIMESTAMP_SECONDS_MIN, start.getSeconds());
assertEquals(0, start.getNanos());
assertEquals(TimeUtil.TIMESTAMP_SECONDS_MAX, end.getSeconds());
assertEquals(999999999, end.getNanos());
assertEquals("0001-01-01T00:00:00Z", TimeUtil.toString(start));
assertEquals("9999-12-31T23:59:59.999999999Z", TimeUtil.toString(end));
Timestamp value = TimeUtil.parseTimestamp("1970-01-01T00:00:00Z");
assertEquals(0, value.getSeconds());
assertEquals(0, value.getNanos());
// Test negative timestamps.
value = TimeUtil.parseTimestamp("1969-12-31T23:59:59.999Z");
assertEquals(-1, value.getSeconds());
// Nano part is in the range of [0, 999999999] for Timestamp.
assertEquals(999000000, value.getNanos());
// Test that 3, 6, or 9 digits are used for the fractional part.
value = Timestamp.newBuilder().setNanos(10).build();
assertEquals("1970-01-01T00:00:00.000000010Z", TimeUtil.toString(value));
value = Timestamp.newBuilder().setNanos(10000).build();
assertEquals("1970-01-01T00:00:00.000010Z", TimeUtil.toString(value));
value = Timestamp.newBuilder().setNanos(10000000).build();
assertEquals("1970-01-01T00:00:00.010Z", TimeUtil.toString(value));
// Test that parsing accepts timezone offsets.
value = TimeUtil.parseTimestamp("1970-01-01T00:00:00.010+08:00");
assertEquals("1969-12-31T16:00:00.010Z", TimeUtil.toString(value));
value = TimeUtil.parseTimestamp("1970-01-01T00:00:00.010-08:00");
assertEquals("1970-01-01T08:00:00.010Z", TimeUtil.toString(value));
}
public void testTimetampInvalidFormat() throws Exception {
try {
// Value too small.
Timestamp value = Timestamp.newBuilder()
.setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
try {
// Value too large.
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();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
try {
// Invalid nanos value.
Timestamp value = Timestamp.newBuilder().setNanos(1000000000).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
try {
// Value to small.
TimeUtil.parseTimestamp("0000-01-01T00:00:00Z");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Value to large.
TimeUtil.parseTimestamp("10000-01-01T00:00:00Z");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Missing 'T'.
TimeUtil.parseTimestamp("1970-01-01 00:00:00Z");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Missing 'Z'.
TimeUtil.parseTimestamp("1970-01-01T00:00:00");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Invalid offset.
TimeUtil.parseTimestamp("1970-01-01T00:00:00+0000");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Trailing text.
TimeUtil.parseTimestamp("1970-01-01T00:00:00Z0");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Invalid nanosecond value.
TimeUtil.parseTimestamp("1970-01-01T00:00:00.ABCZ");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
}
public void testDurationStringFormat() throws Exception {
Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z");
Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z");
Duration duration = TimeUtil.distance(start, end);
assertEquals("315537897599.999999999s", TimeUtil.toString(duration));
duration = TimeUtil.distance(end, start);
assertEquals("-315537897599.999999999s", TimeUtil.toString(duration));
// Generated output should contain 3, 6, or 9 fractional digits.
duration = Duration.newBuilder().setSeconds(1).build();
assertEquals("1s", TimeUtil.toString(duration));
duration = Duration.newBuilder().setNanos(10000000).build();
assertEquals("0.010s", TimeUtil.toString(duration));
duration = Duration.newBuilder().setNanos(10000).build();
assertEquals("0.000010s", TimeUtil.toString(duration));
duration = Duration.newBuilder().setNanos(10).build();
assertEquals("0.000000010s", TimeUtil.toString(duration));
// Parsing accepts an fractional digits as long as they fit into nano
// precision.
duration = TimeUtil.parseDuration("0.1s");
assertEquals(100000000, duration.getNanos());
duration = TimeUtil.parseDuration("0.0001s");
assertEquals(100000, duration.getNanos());
duration = TimeUtil.parseDuration("0.0000001s");
assertEquals(100, duration.getNanos());
// Duration must support range from -315,576,000,000s to +315576000000s
// which includes negative values.
duration = TimeUtil.parseDuration("315576000000.999999999s");
assertEquals(315576000000L, duration.getSeconds());
assertEquals(999999999, duration.getNanos());
duration = TimeUtil.parseDuration("-315576000000.999999999s");
assertEquals(-315576000000L, duration.getSeconds());
assertEquals(-999999999, duration.getNanos());
}
public void testDurationInvalidFormat() throws Exception {
try {
// Value too small.
Duration value = Duration.newBuilder()
.setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
try {
// Value too large.
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();
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();
TimeUtil.toString(value);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
try {
// Value too small.
TimeUtil.parseDuration("-315576000001s");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Value too large.
TimeUtil.parseDuration("315576000001s");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Empty.
TimeUtil.parseDuration("");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Missing "s".
TimeUtil.parseDuration("0");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Invalid trailing data.
TimeUtil.parseDuration("0s0");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
try {
// Invalid prefix.
TimeUtil.parseDuration("--1s");
Assert.fail("Exception is expected.");
} catch (ParseException e) {
// Expected.
}
}
public void testTimestampConversion() throws Exception {
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));
timestamp = TimeUtil.createTimestampFromNanos(1111111111);
assertEquals("1970-01-01T00:00:01.111111111Z", TimeUtil.toString(timestamp));
timestamp = TimeUtil.createTimestampFromMicros(1111111);
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));
assertEquals(-889, TimeUtil.toMillis(timestamp));
timestamp = TimeUtil.createTimestampFromNanos(-888888889);
assertEquals("1969-12-31T23:59:59.111111111Z", TimeUtil.toString(timestamp));
timestamp = TimeUtil.createTimestampFromMicros(-888889);
assertEquals("1969-12-31T23:59:59.111111Z", TimeUtil.toString(timestamp));
timestamp = TimeUtil.createTimestampFromMillis(-889);
assertEquals("1969-12-31T23:59:59.111Z", TimeUtil.toString(timestamp));
}
public void testDurationConversion() throws Exception {
Duration duration = TimeUtil.parseDuration("1.111111111s");
assertEquals(1111111111, TimeUtil.toNanos(duration));
assertEquals(1111111, TimeUtil.toMicros(duration));
assertEquals(1111, TimeUtil.toMillis(duration));
duration = TimeUtil.createDurationFromNanos(1111111111);
assertEquals("1.111111111s", TimeUtil.toString(duration));
duration = TimeUtil.createDurationFromMicros(1111111);
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));
assertEquals(-1111, TimeUtil.toMillis(duration));
duration = TimeUtil.createDurationFromNanos(-1111111111);
assertEquals("-1.111111111s", TimeUtil.toString(duration));
duration = TimeUtil.createDurationFromMicros(-1111111);
assertEquals("-1.111111s", TimeUtil.toString(duration));
duration = TimeUtil.createDurationFromMillis(-1111);
assertEquals("-1.111s", TimeUtil.toString(duration));
}
public void testTimeOperations() throws Exception {
Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z");
Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z");
Duration duration = TimeUtil.distance(start, end);
assertEquals("315537897599.999999999s", TimeUtil.toString(duration));
Timestamp value = TimeUtil.add(start, duration);
assertEquals(end, value);
value = TimeUtil.subtract(end, duration);
assertEquals(start, value);
duration = TimeUtil.distance(end, start);
assertEquals("-315537897599.999999999s", TimeUtil.toString(duration));
value = TimeUtil.add(end, duration);
assertEquals(start, value);
value = TimeUtil.subtract(start, duration);
assertEquals(end, value);
// Result is larger than Long.MAX_VALUE.
try {
duration = TimeUtil.parseDuration("315537897599.999999999s");
duration = TimeUtil.multiply(duration, 315537897599.999999999);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
// Result is lesser than Long.MIN_VALUE.
try {
duration = TimeUtil.parseDuration("315537897599.999999999s");
duration = TimeUtil.multiply(duration, -315537897599.999999999);
Assert.fail("Exception is expected.");
} catch (IllegalArgumentException e) {
// Expected.
}
duration = TimeUtil.parseDuration("-1.125s");
duration = TimeUtil.divide(duration, 2.0);
assertEquals("-0.562500s", TimeUtil.toString(duration));
duration = TimeUtil.multiply(duration, 2.0);
assertEquals("-1.125s", TimeUtil.toString(duration));
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)));
duration = TimeUtil.parseDuration("-0.999999999s");
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)));
// Divisions involving negative values.
//
// (-5) / 2 = -2, remainder = -1
d1 = TimeUtil.parseDuration("-5s");
d2 = TimeUtil.parseDuration("2s");
assertEquals(-2, TimeUtil.divide(d1, d2));
assertEquals(-2, TimeUtil.divide(d1, 2).getSeconds());
assertEquals(-1, TimeUtil.remainder(d1, d2).getSeconds());
// (-5) / (-2) = 2, remainder = -1
d1 = TimeUtil.parseDuration("-5s");
d2 = TimeUtil.parseDuration("-2s");
assertEquals(2, TimeUtil.divide(d1, d2));
assertEquals(2, TimeUtil.divide(d1, -2).getSeconds());
assertEquals(-1, TimeUtil.remainder(d1, d2).getSeconds());
// 5 / (-2) = -2, remainder = 1
d1 = TimeUtil.parseDuration("5s");
d2 = TimeUtil.parseDuration("-2s");
assertEquals(-2, TimeUtil.divide(d1, d2));
assertEquals(-2, TimeUtil.divide(d1, -2).getSeconds());
assertEquals(1, TimeUtil.remainder(d1, d2).getSeconds());
}
}

View File

@ -0,0 +1,158 @@
// 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.
syntax = "proto3";
package json_test;
option java_package = "com.google.protobuf.util";
option java_outer_classname = "JsonTestProto";
import "google/protobuf/any.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
message TestAllTypes {
enum NestedEnum {
FOO = 0;
BAR = 1;
BAZ = 2;
}
message NestedMessage {
int32 value = 1;
}
int32 optional_int32 = 1;
int64 optional_int64 = 2;
uint32 optional_uint32 = 3;
uint64 optional_uint64 = 4;
sint32 optional_sint32 = 5;
sint64 optional_sint64 = 6;
fixed32 optional_fixed32 = 7;
fixed64 optional_fixed64 = 8;
sfixed32 optional_sfixed32 = 9;
sfixed64 optional_sfixed64 = 10;
float optional_float = 11;
double optional_double = 12;
bool optional_bool = 13;
string optional_string = 14;
bytes optional_bytes = 15;
NestedMessage optional_nested_message = 18;
NestedEnum optional_nested_enum = 21;
// Repeated
repeated int32 repeated_int32 = 31;
repeated int64 repeated_int64 = 32;
repeated uint32 repeated_uint32 = 33;
repeated uint64 repeated_uint64 = 34;
repeated sint32 repeated_sint32 = 35;
repeated sint64 repeated_sint64 = 36;
repeated fixed32 repeated_fixed32 = 37;
repeated fixed64 repeated_fixed64 = 38;
repeated sfixed32 repeated_sfixed32 = 39;
repeated sfixed64 repeated_sfixed64 = 40;
repeated float repeated_float = 41;
repeated double repeated_double = 42;
repeated bool repeated_bool = 43;
repeated string repeated_string = 44;
repeated bytes repeated_bytes = 45;
repeated NestedMessage repeated_nested_message = 48;
repeated NestedEnum repeated_nested_enum = 51;
}
message TestMap {
// Instead of testing all combinations (too many), we only make sure all
// valid types have been used at least in one field as key and in one
// field as value.
map<int32, int32> int32_to_int32_map = 1;
map<int64, int32> int64_to_int32_map = 2;
map<uint32, int32> uint32_to_int32_map = 3;
map<uint64, int32> uint64_to_int32_map = 4;
map<sint32, int32> sint32_to_int32_map = 5;
map<sint64, int32> sint64_to_int32_map = 6;
map<fixed32, int32> fixed32_to_int32_map = 7;
map<fixed64, int32> fixed64_to_int32_map = 8;
map<sfixed32, int32> sfixed32_to_int32_map = 9;
map<sfixed64, int32> sfixed64_to_int32_map = 10;
map<bool, int32> bool_to_int32_map = 11;
map<string, int32> string_to_int32_map = 12;
map<int32, int64> int32_to_int64_map = 101;
map<int32, uint32> int32_to_uint32_map = 102;
map<int32, uint64> int32_to_uint64_map = 103;
map<int32, sint32> int32_to_sint32_map = 104;
map<int32, sint64> int32_to_sint64_map = 105;
map<int32, fixed32> int32_to_fixed32_map = 106;
map<int32, fixed64> int32_to_fixed64_map = 107;
map<int32, sfixed32> int32_to_sfixed32_map = 108;
map<int32, sfixed64> int32_to_sfixed64_map = 109;
map<int32, float> int32_to_float_map = 110;
map<int32, double> int32_to_double_map = 111;
map<int32, bool> int32_to_bool_map = 112;
map<int32, string> int32_to_string_map = 113;
map<int32, bytes> int32_to_bytes_map = 114;
map<int32, TestAllTypes.NestedMessage> int32_to_message_map = 115;
map<int32, TestAllTypes.NestedEnum> int32_to_enum_map = 116;
}
message TestWrappers {
google.protobuf.Int32Value int32_value = 1;
google.protobuf.UInt32Value uint32_value = 2;
google.protobuf.Int64Value int64_value = 3;
google.protobuf.UInt64Value uint64_value = 4;
google.protobuf.FloatValue float_value = 5;
google.protobuf.DoubleValue double_value = 6;
google.protobuf.BoolValue bool_value = 7;
google.protobuf.StringValue string_value = 8;
google.protobuf.BytesValue bytes_value = 9;
}
message TestTimestamp {
google.protobuf.Timestamp timestamp_value = 1;
}
message TestDuration {
google.protobuf.Duration duration_value = 1;
}
message TestFieldMask {
google.protobuf.FieldMask field_mask_value = 1;
}
message TestStruct {
google.protobuf.Struct struct_value = 1;
}
message TestAny {
google.protobuf.Any any_value = 1;
}