Change-Id: I83a93fdb119a643fbc884e6ec3624493f6270370pull/735/head
commit
db45aa117a
244 changed files with 13905 additions and 3813 deletions
@ -1,103 +1,103 @@ |
||||
include(GNUInstallDirs) |
||||
|
||||
foreach(_library |
||||
libprotobuf-lite |
||||
libprotobuf |
||||
libprotoc) |
||||
set_property(TARGET ${_library} |
||||
PROPERTY INTERFACE_INCLUDE_DIRECTORIES |
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) |
||||
install(TARGETS ${_library} EXPORT protobuf-targets |
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${_library} |
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library} |
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library}) |
||||
endforeach() |
||||
|
||||
install(TARGETS protoc EXPORT protobuf-targets |
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT protoc) |
||||
|
||||
if(TRUE) |
||||
file(STRINGS extract_includes.bat.in _extract_strings |
||||
REGEX "^copy") |
||||
foreach(_extract_string ${_extract_strings}) |
||||
string(REPLACE "copy \${PROTOBUF_SOURCE_WIN32_PATH}\\" "" |
||||
_extract_string ${_extract_string}) |
||||
string(REPLACE "\\" "/" _extract_string ${_extract_string}) |
||||
string(REGEX MATCH "^[^ ]+" |
||||
_extract_from ${_extract_string}) |
||||
string(REGEX REPLACE "^${_extract_from} ([^$]+)" "\\1" |
||||
_extract_to ${_extract_string}) |
||||
get_filename_component(_extract_from "${protobuf_SOURCE_DIR}/${_extract_from}" ABSOLUTE) |
||||
get_filename_component(_extract_name ${_extract_to} NAME) |
||||
get_filename_component(_extract_to ${_extract_to} PATH) |
||||
string(REPLACE "include/" "${CMAKE_INSTALL_INCLUDEDIR}/" |
||||
_extract_to "${_extract_to}") |
||||
if(EXISTS "${_extract_from}") |
||||
install(FILES "${_extract_from}" |
||||
DESTINATION "${_extract_to}" |
||||
COMPONENT protobuf-headers |
||||
RENAME "${_extract_name}") |
||||
else() |
||||
message(AUTHOR_WARNING "The file \"${_extract_from}\" is listed in " |
||||
"\"${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in\" " |
||||
"but there not exists. The file will not be installed.") |
||||
endif() |
||||
endforeach() |
||||
endif() |
||||
|
||||
# Internal function for parsing auto tools scripts |
||||
function(_protobuf_auto_list FILE_NAME VARIABLE) |
||||
file(STRINGS ${FILE_NAME} _strings) |
||||
set(_list) |
||||
foreach(_string ${_strings}) |
||||
set(_found) |
||||
string(REGEX MATCH "^[ \t]*${VARIABLE}[ \t]*=[ \t]*" _found "${_string}") |
||||
if(_found) |
||||
string(LENGTH "${_found}" _length) |
||||
string(SUBSTRING "${_string}" ${_length} -1 _draft_list) |
||||
foreach(_item ${_draft_list}) |
||||
string(STRIP "${_item}" _item) |
||||
list(APPEND _list "${_item}") |
||||
endforeach() |
||||
endif() |
||||
endforeach() |
||||
set(${VARIABLE} ${_list} PARENT_SCOPE) |
||||
endfunction() |
||||
|
||||
# Install well-known type proto files |
||||
_protobuf_auto_list("../src/Makefile.am" nobase_dist_proto_DATA) |
||||
foreach(_file ${nobase_dist_proto_DATA}) |
||||
get_filename_component(_file_from "../src/${_file}" ABSOLUTE) |
||||
get_filename_component(_file_name ${_file} NAME) |
||||
get_filename_component(_file_path ${_file} PATH) |
||||
if(EXISTS "${_file_from}") |
||||
install(FILES "${_file_from}" |
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_file_path}" |
||||
COMPONENT protobuf-protos |
||||
RENAME "${_file_name}") |
||||
else() |
||||
message(AUTHOR_WARNING "The file \"${_file_from}\" is listed in " |
||||
"\"${protobuf_SOURCE_DIR}/../src/Makefile.am\" as nobase_dist_proto_DATA " |
||||
"but there not exists. The file will not be installed.") |
||||
endif() |
||||
endforeach() |
||||
|
||||
# Export configuration |
||||
|
||||
install(EXPORT protobuf-targets |
||||
DESTINATION "lib/cmake/protobuf" |
||||
COMPONENT protobuf-export) |
||||
|
||||
configure_file(protobuf-config.cmake.in |
||||
protobuf-config.cmake @ONLY) |
||||
configure_file(protobuf-config-version.cmake.in |
||||
protobuf-config-version.cmake @ONLY) |
||||
configure_file(protobuf-module.cmake.in |
||||
protobuf-module.cmake @ONLY) |
||||
|
||||
install(FILES |
||||
"${protobuf_BINARY_DIR}/protobuf-config.cmake" |
||||
"${protobuf_BINARY_DIR}/protobuf-config-version.cmake" |
||||
"${protobuf_BINARY_DIR}/protobuf-module.cmake" |
||||
DESTINATION "lib/cmake/protobuf" |
||||
COMPONENT protobuf-export) |
||||
include(GNUInstallDirs) |
||||
|
||||
foreach(_library |
||||
libprotobuf-lite |
||||
libprotobuf |
||||
libprotoc) |
||||
set_property(TARGET ${_library} |
||||
PROPERTY INTERFACE_INCLUDE_DIRECTORIES |
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) |
||||
install(TARGETS ${_library} EXPORT protobuf-targets |
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${_library} |
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library} |
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library}) |
||||
endforeach() |
||||
|
||||
install(TARGETS protoc EXPORT protobuf-targets |
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT protoc) |
||||
|
||||
if(TRUE) |
||||
file(STRINGS extract_includes.bat.in _extract_strings |
||||
REGEX "^copy") |
||||
foreach(_extract_string ${_extract_strings}) |
||||
string(REPLACE "copy \${PROTOBUF_SOURCE_WIN32_PATH}\\" "" |
||||
_extract_string ${_extract_string}) |
||||
string(REPLACE "\\" "/" _extract_string ${_extract_string}) |
||||
string(REGEX MATCH "^[^ ]+" |
||||
_extract_from ${_extract_string}) |
||||
string(REGEX REPLACE "^${_extract_from} ([^$]+)" "\\1" |
||||
_extract_to ${_extract_string}) |
||||
get_filename_component(_extract_from "${protobuf_SOURCE_DIR}/${_extract_from}" ABSOLUTE) |
||||
get_filename_component(_extract_name ${_extract_to} NAME) |
||||
get_filename_component(_extract_to ${_extract_to} PATH) |
||||
string(REPLACE "include/" "${CMAKE_INSTALL_INCLUDEDIR}/" |
||||
_extract_to "${_extract_to}") |
||||
if(EXISTS "${_extract_from}") |
||||
install(FILES "${_extract_from}" |
||||
DESTINATION "${_extract_to}" |
||||
COMPONENT protobuf-headers |
||||
RENAME "${_extract_name}") |
||||
else() |
||||
message(AUTHOR_WARNING "The file \"${_extract_from}\" is listed in " |
||||
"\"${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in\" " |
||||
"but there not exists. The file will not be installed.") |
||||
endif() |
||||
endforeach() |
||||
endif() |
||||
|
||||
# Internal function for parsing auto tools scripts |
||||
function(_protobuf_auto_list FILE_NAME VARIABLE) |
||||
file(STRINGS ${FILE_NAME} _strings) |
||||
set(_list) |
||||
foreach(_string ${_strings}) |
||||
set(_found) |
||||
string(REGEX MATCH "^[ \t]*${VARIABLE}[ \t]*=[ \t]*" _found "${_string}") |
||||
if(_found) |
||||
string(LENGTH "${_found}" _length) |
||||
string(SUBSTRING "${_string}" ${_length} -1 _draft_list) |
||||
foreach(_item ${_draft_list}) |
||||
string(STRIP "${_item}" _item) |
||||
list(APPEND _list "${_item}") |
||||
endforeach() |
||||
endif() |
||||
endforeach() |
||||
set(${VARIABLE} ${_list} PARENT_SCOPE) |
||||
endfunction() |
||||
|
||||
# Install well-known type proto files |
||||
_protobuf_auto_list("../src/Makefile.am" nobase_dist_proto_DATA) |
||||
foreach(_file ${nobase_dist_proto_DATA}) |
||||
get_filename_component(_file_from "../src/${_file}" ABSOLUTE) |
||||
get_filename_component(_file_name ${_file} NAME) |
||||
get_filename_component(_file_path ${_file} PATH) |
||||
if(EXISTS "${_file_from}") |
||||
install(FILES "${_file_from}" |
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_file_path}" |
||||
COMPONENT protobuf-protos |
||||
RENAME "${_file_name}") |
||||
else() |
||||
message(AUTHOR_WARNING "The file \"${_file_from}\" is listed in " |
||||
"\"${protobuf_SOURCE_DIR}/../src/Makefile.am\" as nobase_dist_proto_DATA " |
||||
"but there not exists. The file will not be installed.") |
||||
endif() |
||||
endforeach() |
||||
|
||||
# Export configuration |
||||
|
||||
install(EXPORT protobuf-targets |
||||
DESTINATION "lib/cmake/protobuf" |
||||
COMPONENT protobuf-export) |
||||
|
||||
configure_file(protobuf-config.cmake.in |
||||
protobuf-config.cmake @ONLY) |
||||
configure_file(protobuf-config-version.cmake.in |
||||
protobuf-config-version.cmake @ONLY) |
||||
configure_file(protobuf-module.cmake.in |
||||
protobuf-module.cmake @ONLY) |
||||
|
||||
install(FILES |
||||
"${protobuf_BINARY_DIR}/protobuf-config.cmake" |
||||
"${protobuf_BINARY_DIR}/protobuf-config-version.cmake" |
||||
"${protobuf_BINARY_DIR}/protobuf-module.cmake" |
||||
DESTINATION "lib/cmake/protobuf" |
||||
COMPONENT protobuf-export) |
||||
|
@ -1 +1 @@ |
||||
set(PACKAGE_VERSION @protobuf_VERSION@) |
||||
set(PACKAGE_VERSION @protobuf_VERSION@) |
||||
|
@ -1,27 +1,27 @@ |
||||
# Version info variables |
||||
set(PROTOBUF_VERSION "@protobuf_VERSION@") |
||||
set(PROTOBUF_VERSION_STRING "@protobuf_VERSION_STRING@") |
||||
|
||||
# Current dir |
||||
get_filename_component(_PROTOBUF_PACKAGE_PREFIX |
||||
"${CMAKE_CURRENT_LIST_FILE}" PATH) |
||||
|
||||
# Imported targets |
||||
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-targets.cmake") |
||||
|
||||
# Compute the installation prefix relative to this file. |
||||
get_filename_component(_PROTOBUF_IMPORT_PREFIX |
||||
"${_PROTOBUF_PACKAGE_PREFIX}" PATH) |
||||
get_filename_component(_PROTOBUF_IMPORT_PREFIX |
||||
"${_PROTOBUF_IMPORT_PREFIX}" PATH) |
||||
get_filename_component(_PROTOBUF_IMPORT_PREFIX |
||||
"${_PROTOBUF_IMPORT_PREFIX}" PATH) |
||||
|
||||
# CMake FindProtobuf module compatible file |
||||
if(NOT DEFINED PROTOBUF_MODULE_COMPATIBLE OR "${PROTOBUF_MODULE_COMPATIBLE}") |
||||
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-module.cmake") |
||||
endif() |
||||
|
||||
# Cleanup temporary variables. |
||||
set(_PROTOBUF_PACKAGE_PREFIX) |
||||
set(_PROTOBUF_IMPORT_PREFIX) |
||||
# Version info variables |
||||
set(PROTOBUF_VERSION "@protobuf_VERSION@") |
||||
set(PROTOBUF_VERSION_STRING "@protobuf_VERSION_STRING@") |
||||
|
||||
# Current dir |
||||
get_filename_component(_PROTOBUF_PACKAGE_PREFIX |
||||
"${CMAKE_CURRENT_LIST_FILE}" PATH) |
||||
|
||||
# Imported targets |
||||
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-targets.cmake") |
||||
|
||||
# Compute the installation prefix relative to this file. |
||||
get_filename_component(_PROTOBUF_IMPORT_PREFIX |
||||
"${_PROTOBUF_PACKAGE_PREFIX}" PATH) |
||||
get_filename_component(_PROTOBUF_IMPORT_PREFIX |
||||
"${_PROTOBUF_IMPORT_PREFIX}" PATH) |
||||
get_filename_component(_PROTOBUF_IMPORT_PREFIX |
||||
"${_PROTOBUF_IMPORT_PREFIX}" PATH) |
||||
|
||||
# CMake FindProtobuf module compatible file |
||||
if(NOT DEFINED PROTOBUF_MODULE_COMPATIBLE OR "${PROTOBUF_MODULE_COMPATIBLE}") |
||||
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-module.cmake") |
||||
endif() |
||||
|
||||
# Cleanup temporary variables. |
||||
set(_PROTOBUF_PACKAGE_PREFIX) |
||||
set(_PROTOBUF_IMPORT_PREFIX) |
||||
|
@ -0,0 +1,92 @@ |
||||
// 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; |
||||
|
||||
import any_test.AnyTestProto.TestAny; |
||||
import protobuf_unittest.UnittestProto.TestAllTypes; |
||||
|
||||
import junit.framework.TestCase; |
||||
|
||||
/** |
||||
* Unit tests for Any message. |
||||
*/ |
||||
public class AnyTest extends TestCase { |
||||
public void testAnyGeneratedApi() throws Exception { |
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder(); |
||||
TestUtil.setAllFields(builder); |
||||
TestAllTypes message = builder.build(); |
||||
|
||||
TestAny container = TestAny.newBuilder() |
||||
.setValue(Any.pack(message)).build(); |
||||
|
||||
assertTrue(container.getValue().is(TestAllTypes.class)); |
||||
assertFalse(container.getValue().is(TestAny.class)); |
||||
|
||||
TestAllTypes result = container.getValue().unpack(TestAllTypes.class); |
||||
TestUtil.assertAllFieldsSet(result); |
||||
|
||||
|
||||
// Unpacking to a wrong type will throw an exception.
|
||||
try { |
||||
TestAny wrongMessage = container.getValue().unpack(TestAny.class); |
||||
fail("Exception is expected."); |
||||
} catch (InvalidProtocolBufferException e) { |
||||
// expected.
|
||||
} |
||||
|
||||
// Test that unpacking throws an exception if parsing fails.
|
||||
TestAny.Builder containerBuilder = container.toBuilder(); |
||||
containerBuilder.getValueBuilder().setValue( |
||||
ByteString.copyFrom(new byte[]{0x11})); |
||||
container = containerBuilder.build(); |
||||
try { |
||||
TestAllTypes parsingFailed = container.getValue().unpack(TestAllTypes.class); |
||||
fail("Exception is expected."); |
||||
} catch (InvalidProtocolBufferException e) { |
||||
// expected.
|
||||
} |
||||
} |
||||
|
||||
public void testCachedUnpackResult() throws Exception { |
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder(); |
||||
TestUtil.setAllFields(builder); |
||||
TestAllTypes message = builder.build(); |
||||
|
||||
TestAny container = TestAny.newBuilder() |
||||
.setValue(Any.pack(message)).build(); |
||||
|
||||
assertTrue(container.getValue().is(TestAllTypes.class)); |
||||
|
||||
TestAllTypes result1 = container.getValue().unpack(TestAllTypes.class); |
||||
TestAllTypes result2 = container.getValue().unpack(TestAllTypes.class); |
||||
assertTrue(result1 == result2); |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
// 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 any_test; |
||||
|
||||
option java_package = "any_test"; |
||||
option java_outer_classname = "AnyTestProto"; |
||||
|
||||
import "google/protobuf/any.proto"; |
||||
|
||||
message TestAny { |
||||
google.protobuf.Any value = 1; |
||||
} |
@ -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> |
@ -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)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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
@ -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; |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
} |
||||
|
@ -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()); |
||||
} |
||||
} |
@ -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.
|
||||
} |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -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; |
||||
} |
@ -0,0 +1,73 @@ |
||||
// 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 google.protobuf.python.internal; |
||||
|
||||
message TestPackedTypes { |
||||
enum NestedEnum { |
||||
FOO = 0; |
||||
BAR = 1; |
||||
BAZ = 2; |
||||
} |
||||
|
||||
repeated int32 repeated_int32 = 1; |
||||
repeated int64 repeated_int64 = 2; |
||||
repeated uint32 repeated_uint32 = 3; |
||||
repeated uint64 repeated_uint64 = 4; |
||||
repeated sint32 repeated_sint32 = 5; |
||||
repeated sint64 repeated_sint64 = 6; |
||||
repeated fixed32 repeated_fixed32 = 7; |
||||
repeated fixed64 repeated_fixed64 = 8; |
||||
repeated sfixed32 repeated_sfixed32 = 9; |
||||
repeated sfixed64 repeated_sfixed64 = 10; |
||||
repeated float repeated_float = 11; |
||||
repeated double repeated_double = 12; |
||||
repeated bool repeated_bool = 13; |
||||
repeated NestedEnum repeated_nested_enum = 14; |
||||
} |
||||
|
||||
message TestUnpackedTypes { |
||||
repeated int32 repeated_int32 = 1 [packed = false]; |
||||
repeated int64 repeated_int64 = 2 [packed = false]; |
||||
repeated uint32 repeated_uint32 = 3 [packed = false]; |
||||
repeated uint64 repeated_uint64 = 4 [packed = false]; |
||||
repeated sint32 repeated_sint32 = 5 [packed = false]; |
||||
repeated sint64 repeated_sint64 = 6 [packed = false]; |
||||
repeated fixed32 repeated_fixed32 = 7 [packed = false]; |
||||
repeated fixed64 repeated_fixed64 = 8 [packed = false]; |
||||
repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; |
||||
repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; |
||||
repeated float repeated_float = 11 [packed = false]; |
||||
repeated double repeated_double = 12 [packed = false]; |
||||
repeated bool repeated_bool = 13 [packed = false]; |
||||
repeated TestPackedTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue