Add dependencies locally
This commit is contained in:
167
deps/protobuf/java/util/pom.xml
vendored
Normal file
167
deps/protobuf/java/util/pom.xml
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
<?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.protobuf</groupId>
|
||||
<artifactId>protobuf-parent</artifactId>
|
||||
<version>3.20.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>protobuf-java-util</artifactId>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<name>Protocol Buffers [Util]</name>
|
||||
<description>Utilities for Protocol Buffers</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
<version>2.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.j2objc</groupId>
|
||||
<artifactId>j2objc-annotations</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava-testlib</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.truth</groupId>
|
||||
<artifactId>truth</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<!-- Use the core proto dir so that we can call the core generation script -->
|
||||
<test.proto.dir>../core/src/test/proto</test.proto.dir>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- Generate the test protos -->
|
||||
<execution>
|
||||
<id>generate-test-sources</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<!-- Generate all of the test protos from the core module -->
|
||||
<ant antfile="../core/generate-test-sources-build.xml"/>
|
||||
|
||||
<!-- Generate additional test protos for this module -->
|
||||
<exec executable="${protoc}">
|
||||
<arg value="--java_out=${generated.testsources.dir}"/>
|
||||
<arg value="--proto_path=${protobuf.source.dir}"/>
|
||||
<arg value="--proto_path=src/test/proto"/>
|
||||
<arg value="src/test/proto/com/google/protobuf/util/json_test.proto"/>
|
||||
</exec>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Add the generated test sources to the build -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-generated-test-sources</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${generated.testsources.dir}</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>net.sf.androidscents.signature</groupId>
|
||||
<artifactId>android-api-level-19</artifactId>
|
||||
<version>4.4.2_r4</version>
|
||||
</signature>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>android</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Configure the OSGI bundle -->
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Automatic-Module-Name>com.google.protobuf.util</Automatic-Module-Name> <!-- Java9+ Jigsaw module name -->
|
||||
<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=${project.version}</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Configure the fat jar to include all dependencies -->
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
18
deps/protobuf/java/util/pom_template.xml
vendored
Normal file
18
deps/protobuf/java/util/pom_template.xml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?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>{groupId}</groupId>
|
||||
<artifactId>protobuf-parent</artifactId>
|
||||
<version>{version}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>{artifactId}</artifactId>
|
||||
<packaging>{type}</packaging>
|
||||
|
||||
<name>Protocol Buffers [Util]</name>
|
||||
<description>Utilities for Protocol Buffers</description>
|
||||
|
||||
{dependencies}
|
||||
|
||||
</project>
|
481
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Durations.java
vendored
Normal file
481
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Durations.java
vendored
Normal file
@ -0,0 +1,481 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.math.IntMath.checkedAdd;
|
||||
import static com.google.common.math.IntMath.checkedSubtract;
|
||||
import static com.google.common.math.LongMath.checkedAdd;
|
||||
import static com.google.common.math.LongMath.checkedMultiply;
|
||||
import static com.google.common.math.LongMath.checkedSubtract;
|
||||
import static com.google.protobuf.util.Timestamps.MICROS_PER_SECOND;
|
||||
import static com.google.protobuf.util.Timestamps.MILLIS_PER_SECOND;
|
||||
import static com.google.protobuf.util.Timestamps.NANOS_PER_MICROSECOND;
|
||||
import static com.google.protobuf.util.Timestamps.NANOS_PER_MILLISECOND;
|
||||
import static com.google.protobuf.util.Timestamps.NANOS_PER_SECOND;
|
||||
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.annotations.CompileTimeConstant;
|
||||
import com.google.protobuf.Duration;
|
||||
import java.io.Serializable;
|
||||
import java.text.ParseException;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Utilities to help create/manipulate {@code protobuf/duration.proto}. All operations throw an
|
||||
* {@link IllegalArgumentException} if the input(s) are not {@linkplain #isValid(Duration) valid}.
|
||||
*/
|
||||
public final class Durations {
|
||||
static final long DURATION_SECONDS_MIN = -315576000000L;
|
||||
static final long DURATION_SECONDS_MAX = 315576000000L;
|
||||
|
||||
private static final long SECONDS_PER_MINUTE = 60L;
|
||||
private static final long SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60;
|
||||
private static final long SECONDS_PER_DAY = SECONDS_PER_HOUR * 24;
|
||||
|
||||
/** A constant holding the minimum valid {@link Duration}, approximately {@code -10,000} years. */
|
||||
public static final Duration MIN_VALUE =
|
||||
Duration.newBuilder().setSeconds(DURATION_SECONDS_MIN).setNanos(-999999999).build();
|
||||
|
||||
/** A constant holding the maximum valid {@link Duration}, approximately {@code +10,000} years. */
|
||||
public static final Duration MAX_VALUE =
|
||||
Duration.newBuilder().setSeconds(DURATION_SECONDS_MAX).setNanos(999999999).build();
|
||||
|
||||
/** A constant holding the duration of zero. */
|
||||
public static final Duration ZERO = Duration.newBuilder().setSeconds(0L).setNanos(0).build();
|
||||
|
||||
private Durations() {}
|
||||
|
||||
private static enum DurationComparator implements Comparator<Duration>, Serializable {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public int compare(Duration d1, Duration d2) {
|
||||
checkValid(d1);
|
||||
checkValid(d2);
|
||||
int secDiff = Long.compare(d1.getSeconds(), d2.getSeconds());
|
||||
return (secDiff != 0) ? secDiff : Integer.compare(d1.getNanos(), d2.getNanos());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Comparator} for {@link Duration}s which sorts in increasing chronological
|
||||
* order. Nulls and invalid {@link Duration}s are not allowed (see {@link #isValid}). The returned
|
||||
* comparator is serializable.
|
||||
*/
|
||||
public static Comparator<Duration> comparator() {
|
||||
return DurationComparator.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two durations. The value returned is identical to what would be returned by: {@code
|
||||
* Durations.comparator().compare(x, y)}.
|
||||
*
|
||||
* @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if {@code x < y};
|
||||
* and a value greater than {@code 0} if {@code x > y}
|
||||
*/
|
||||
public static int compare(Duration x, Duration y) {
|
||||
return DurationComparator.INSTANCE.compare(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the
|
||||
* range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range
|
||||
* [-999,999,999, +999,999,999].
|
||||
*
|
||||
* <p><b>Note:</b> Durations less than one second are represented with a 0 {@code seconds} field
|
||||
* and a positive or negative {@code nanos} field. For durations of one second or more, a non-zero
|
||||
* value for the {@code nanos} field must be of the same sign as the {@code seconds} field.
|
||||
*/
|
||||
public static boolean isValid(Duration duration) {
|
||||
return isValid(duration.getSeconds(), duration.getNanos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given number of seconds and nanos is a valid {@link Duration}. The {@code
|
||||
* seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The {@code nanos}
|
||||
* value must be in the range [-999,999,999, +999,999,999].
|
||||
*
|
||||
* <p><b>Note:</b> Durations less than one second are represented with a 0 {@code seconds} field
|
||||
* and a positive or negative {@code nanos} field. For durations of one second or more, a non-zero
|
||||
* value for the {@code nanos} field must be of the same sign as the {@code seconds} field.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static boolean isValid(long seconds, int nanos) {
|
||||
if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (nanos < -999999999L || nanos >= NANOS_PER_SECOND) {
|
||||
return false;
|
||||
}
|
||||
if (seconds < 0 || nanos < 0) {
|
||||
if (seconds > 0 || nanos > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns whether the given {@link Duration} is negative or not. */
|
||||
public static boolean isNegative(Duration duration) {
|
||||
checkValid(duration);
|
||||
return (duration.getSeconds() == 0) ? duration.getNanos() < 0 : duration.getSeconds() < 0;
|
||||
}
|
||||
|
||||
/** Returns whether the given {@link Duration} is positive or not. */
|
||||
public static boolean isPositive(Duration duration) {
|
||||
checkValid(duration);
|
||||
return !isNegative(duration) && !duration.equals(ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given {@link Duration} is not negative.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code duration} is negative or invalid
|
||||
* @throws NullPointerException if {@code duration} is {@code null}
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public static Duration checkNotNegative(Duration duration) {
|
||||
checkArgument(!isNegative(duration), "duration (%s) must not be negative", toString(duration));
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given {@link Duration} is positive.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code duration} is negative, {@code ZERO}, or invalid
|
||||
* @throws NullPointerException if {@code duration} is {@code null}
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public static Duration checkPositive(Duration duration) {
|
||||
checkArgument(isPositive(duration), "duration (%s) must be positive", toString(duration));
|
||||
return duration;
|
||||
}
|
||||
|
||||
/** Throws an {@link IllegalArgumentException} if the given {@link Duration} is not valid. */
|
||||
@CanIgnoreReturnValue
|
||||
public static Duration checkValid(Duration duration) {
|
||||
long seconds = duration.getSeconds();
|
||||
int nanos = duration.getNanos();
|
||||
if (!isValid(seconds, nanos)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Duration is not valid. See proto definition for valid values. "
|
||||
+ "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. "
|
||||
+ "Nanos (%s) must be in range [-999,999,999, +999,999,999]. "
|
||||
+ "Nanos must have the same sign as seconds",
|
||||
seconds, nanos));
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the given builder and throws an {@link IllegalArgumentException} if it is not valid. See
|
||||
* {@link #checkValid(Duration)}.
|
||||
*
|
||||
* @return A valid, built {@link Duration}.
|
||||
*/
|
||||
public static Duration checkValid(Duration.Builder durationBuilder) {
|
||||
return checkValid(durationBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
checkValid(duration);
|
||||
|
||||
long seconds = duration.getSeconds();
|
||||
int nanos = duration.getNanos();
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (seconds < 0 || nanos < 0) {
|
||||
result.append("-");
|
||||
seconds = -seconds;
|
||||
nanos = -nanos;
|
||||
}
|
||||
result.append(seconds);
|
||||
if (nanos != 0) {
|
||||
result.append(".");
|
||||
result.append(Timestamps.formatNanos(nanos));
|
||||
}
|
||||
result.append("s");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string to produce a duration.
|
||||
*
|
||||
* @return a Duration parsed from the string
|
||||
* @throws ParseException if the string is not in the duration format
|
||||
*/
|
||||
public static Duration parse(String value) throws ParseException {
|
||||
// Must end with "s".
|
||||
if (value.isEmpty() || value.charAt(value.length() - 1) != 's') {
|
||||
throw new ParseException("Invalid duration string: " + value, 0);
|
||||
}
|
||||
boolean negative = false;
|
||||
if (value.charAt(0) == '-') {
|
||||
negative = true;
|
||||
value = value.substring(1);
|
||||
}
|
||||
String secondValue = value.substring(0, value.length() - 1);
|
||||
String nanoValue = "";
|
||||
int pointPosition = secondValue.indexOf('.');
|
||||
if (pointPosition != -1) {
|
||||
nanoValue = secondValue.substring(pointPosition + 1);
|
||||
secondValue = secondValue.substring(0, pointPosition);
|
||||
}
|
||||
long seconds = Long.parseLong(secondValue);
|
||||
int nanos = nanoValue.isEmpty() ? 0 : Timestamps.parseNanos(nanoValue);
|
||||
if (seconds < 0) {
|
||||
throw new ParseException("Invalid duration string: " + value, 0);
|
||||
}
|
||||
if (negative) {
|
||||
seconds = -seconds;
|
||||
nanos = -nanos;
|
||||
}
|
||||
try {
|
||||
return normalizedDuration(seconds, nanos);
|
||||
} catch (IllegalArgumentException e) {
|
||||
ParseException ex = new ParseException("Duration value is out of range.", 0);
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string in RFC 3339 format into a {@link Duration}.
|
||||
*
|
||||
* <p>Identical to {@link #parse(String)}, but throws an {@link IllegalArgumentException} instead
|
||||
* of a {@link ParseException} if parsing fails.
|
||||
*
|
||||
* @return a {@link Duration} parsed from the string
|
||||
* @throws IllegalArgumentException if parsing fails
|
||||
*/
|
||||
public static Duration parseUnchecked(@CompileTimeConstant String value) {
|
||||
try {
|
||||
return parse(value);
|
||||
} catch (ParseException e) {
|
||||
// While `java.time.format.DateTimeParseException` is a more accurate representation of the
|
||||
// failure, this library is currently not JDK8 ready because of Android dependencies.
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Static factories
|
||||
|
||||
/** Create a Duration from the number of days. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromDays(long days) {
|
||||
return Duration.newBuilder()
|
||||
.setSeconds(checkedMultiply(days, SECONDS_PER_DAY))
|
||||
.setNanos(0)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Create a Duration from the number of hours. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromHours(long hours) {
|
||||
return Duration.newBuilder()
|
||||
.setSeconds(checkedMultiply(hours, SECONDS_PER_HOUR))
|
||||
.setNanos(0)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Create a Duration from the number of minutes. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromMinutes(long minutes) {
|
||||
return Duration.newBuilder()
|
||||
.setSeconds(checkedMultiply(minutes, SECONDS_PER_MINUTE))
|
||||
.setNanos(0)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Create a Duration from the number of seconds. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromSeconds(long seconds) {
|
||||
return normalizedDuration(seconds, 0);
|
||||
}
|
||||
|
||||
/** Create a Duration from the number of milliseconds. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromMillis(long milliseconds) {
|
||||
return normalizedDuration(
|
||||
milliseconds / MILLIS_PER_SECOND,
|
||||
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
|
||||
}
|
||||
|
||||
/** Create a Duration from the number of microseconds. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromMicros(long microseconds) {
|
||||
return normalizedDuration(
|
||||
microseconds / MICROS_PER_SECOND,
|
||||
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
|
||||
}
|
||||
|
||||
/** Create a Duration from the number of nanoseconds. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Duration fromNanos(long nanoseconds) {
|
||||
return normalizedDuration(
|
||||
nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND));
|
||||
}
|
||||
|
||||
// Conversion APIs
|
||||
|
||||
/**
|
||||
* Convert a Duration to the number of days. The result will be rounded towards 0 to the nearest
|
||||
* day.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toDays(Duration duration) {
|
||||
return checkValid(duration).getSeconds() / SECONDS_PER_DAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Duration to the number of hours. The result will be rounded towards 0 to the nearest
|
||||
* hour.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toHours(Duration duration) {
|
||||
return checkValid(duration).getSeconds() / SECONDS_PER_HOUR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Duration to the number of minutes. The result will be rounded towards 0 to the
|
||||
* nearest minute.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toMinutes(Duration duration) {
|
||||
return checkValid(duration).getSeconds() / SECONDS_PER_MINUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Duration to the number of seconds. The result will be rounded towards 0 to the
|
||||
* nearest second. E.g., if the duration represents -1 nanosecond, it will be rounded to 0.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toSeconds(Duration duration) {
|
||||
return checkValid(duration).getSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of seconds of the given duration as a {@code double}. This method should be
|
||||
* used to accommodate APIs that <b>only</b> accept durations as {@code double} values.
|
||||
*
|
||||
* <p>This conversion may lose precision.
|
||||
*
|
||||
* <p>If you need the number of seconds in this duration as a {@code long} (not a {@code double}),
|
||||
* simply use {@code duration.getSeconds()} or {@link #toSeconds} (which includes validation).
|
||||
*/
|
||||
@SuppressWarnings({
|
||||
"DurationSecondsToDouble", // that's the whole point of this method
|
||||
"GoodTime" // this is a legacy conversion API
|
||||
})
|
||||
public static double toSecondsAsDouble(Duration duration) {
|
||||
checkValid(duration);
|
||||
return duration.getSeconds() + duration.getNanos() / 1e9;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toMillis(Duration duration) {
|
||||
checkValid(duration);
|
||||
return checkedAdd(
|
||||
checkedMultiply(duration.getSeconds(), MILLIS_PER_SECOND),
|
||||
duration.getNanos() / NANOS_PER_MILLISECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toMicros(Duration duration) {
|
||||
checkValid(duration);
|
||||
return checkedAdd(
|
||||
checkedMultiply(duration.getSeconds(), MICROS_PER_SECOND),
|
||||
duration.getNanos() / NANOS_PER_MICROSECOND);
|
||||
}
|
||||
|
||||
/** Convert a Duration to the number of nanoseconds. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toNanos(Duration duration) {
|
||||
checkValid(duration);
|
||||
return checkedAdd(
|
||||
checkedMultiply(duration.getSeconds(), NANOS_PER_SECOND), duration.getNanos());
|
||||
}
|
||||
|
||||
// Math operations
|
||||
|
||||
/** Add two durations. */
|
||||
public static Duration add(Duration d1, Duration d2) {
|
||||
checkValid(d1);
|
||||
checkValid(d2);
|
||||
return normalizedDuration(
|
||||
checkedAdd(d1.getSeconds(), d2.getSeconds()), checkedAdd(d1.getNanos(), d2.getNanos()));
|
||||
}
|
||||
|
||||
/** Subtract a duration from another. */
|
||||
public static Duration subtract(Duration d1, Duration d2) {
|
||||
checkValid(d1);
|
||||
checkValid(d2);
|
||||
return normalizedDuration(
|
||||
checkedSubtract(d1.getSeconds(), d2.getSeconds()),
|
||||
checkedSubtract(d1.getNanos(), d2.getNanos()));
|
||||
}
|
||||
|
||||
static Duration normalizedDuration(long seconds, int nanos) {
|
||||
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
|
||||
seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND);
|
||||
nanos %= NANOS_PER_SECOND;
|
||||
}
|
||||
if (seconds > 0 && nanos < 0) {
|
||||
nanos += NANOS_PER_SECOND; // no overflow since nanos is negative (and we're adding)
|
||||
seconds--; // no overflow since seconds is positive (and we're decrementing)
|
||||
}
|
||||
if (seconds < 0 && nanos > 0) {
|
||||
nanos -= NANOS_PER_SECOND; // no overflow since nanos is positive (and we're subtracting)
|
||||
seconds++; // no overflow since seconds is negative (and we're incrementing)
|
||||
}
|
||||
Duration duration = Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build();
|
||||
return checkValid(duration);
|
||||
}
|
||||
}
|
339
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
vendored
Normal file
339
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
// 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.common.base.Splitter;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.FieldMask;
|
||||
import com.google.protobuf.GeneratedMessage;
|
||||
import com.google.protobuf.Message;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
final class FieldMaskTree {
|
||||
private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
|
||||
|
||||
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
|
||||
|
||||
private static final class Node {
|
||||
final SortedMap<String, Node> children = new TreeMap<>();
|
||||
}
|
||||
|
||||
private final Node root = new Node();
|
||||
|
||||
/** Creates an empty FieldMaskTree. */
|
||||
FieldMaskTree() {}
|
||||
|
||||
/** Creates a FieldMaskTree for a given FieldMask. */
|
||||
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.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
@SuppressWarnings("StringSplitter")
|
||||
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. */
|
||||
@CanIgnoreReturnValue
|
||||
FieldMaskTree mergeFromFieldMask(FieldMask mask) {
|
||||
for (String path : mask.getPathsList()) {
|
||||
addFieldPath(path);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code path} from the tree.
|
||||
*
|
||||
* <ul>
|
||||
* When removing a field path from the tree:
|
||||
* <li>All sub-paths will be removed. That is, after removing "foo.bar" from the tree,
|
||||
* "foo.bar.baz" will be removed.
|
||||
* <li>If all children of a node have been removed, the node itself will be removed as well.
|
||||
* That is, if "foo" only has one child "bar" and "foo.bar" only has one child "baz",
|
||||
* removing "foo.bar.barz" would remove both "foo" and "foo.bar". If "foo" has both "bar"
|
||||
* and "qux" as children, removing "foo.bar" would leave the path "foo.qux" intact.
|
||||
* <li>If the field path to remove is a non-exist sub-path, nothing will be changed.
|
||||
* </ul>
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
FieldMaskTree removeFieldPath(String path) {
|
||||
List<String> parts = Splitter.onPattern(FIELD_PATH_SEPARATOR_REGEX).splitToList(path);
|
||||
if (parts.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
removeFieldPath(root, parts, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code parts} from {@code node} recursively.
|
||||
*
|
||||
* @return a boolean value indicating whether current {@code node} should be removed.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
private static boolean removeFieldPath(Node node, List<String> parts, int index) {
|
||||
String key = parts.get(index);
|
||||
|
||||
// Base case 1: path not match.
|
||||
if (!node.children.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
// Base case 2: last element in parts.
|
||||
if (index == parts.size() - 1) {
|
||||
node.children.remove(key);
|
||||
return node.children.isEmpty();
|
||||
}
|
||||
// Recursive remove sub-path.
|
||||
if (removeFieldPath(node.children.get(key), parts, index + 1)) {
|
||||
node.children.remove(key);
|
||||
}
|
||||
return node.children.isEmpty();
|
||||
}
|
||||
|
||||
/** Removes all field paths in {@code mask} from this tree. */
|
||||
@CanIgnoreReturnValue
|
||||
FieldMaskTree removeFromFieldMask(FieldMask mask) {
|
||||
for (String path : mask.getPathsList()) {
|
||||
removeFieldPath(path);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Converts this tree to a FieldMask. */
|
||||
FieldMask toFieldMask() {
|
||||
if (root.children.isEmpty()) {
|
||||
return FieldMask.getDefaultInstance();
|
||||
}
|
||||
List<String> paths = new ArrayList<>();
|
||||
getFieldPaths(root, "", paths);
|
||||
return FieldMask.newBuilder().addAllPaths(paths).build();
|
||||
}
|
||||
|
||||
/** Gathers all field paths in a sub-tree. */
|
||||
private static 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}. */
|
||||
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<>();
|
||||
getFieldPaths(node, path, paths);
|
||||
for (String value : paths) {
|
||||
output.addFieldPath(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all fields specified by this FieldMaskTree from {@code source} to {@code destination}.
|
||||
*/
|
||||
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 static void merge(
|
||||
Node node, Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
|
||||
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"source (%s) and destination (%s) descriptor must be equal",
|
||||
source.getDescriptorForType().getFullName(),
|
||||
destination.getDescriptorForType().getFullName()));
|
||||
}
|
||||
|
||||
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 "
|
||||
+ "singular message field and cannot have sub-fields.");
|
||||
continue;
|
||||
}
|
||||
if (!source.hasField(field) && !destination.hasField(field)) {
|
||||
// If the message field is not present in both source and destination, skip recursing
|
||||
// so we don't create unnecessary empty messages.
|
||||
continue;
|
||||
}
|
||||
// This is a mess because of java proto API 1 still hanging around.
|
||||
Message.Builder childBuilder =
|
||||
destination instanceof GeneratedMessage.Builder
|
||||
? destination.getFieldBuilder(field)
|
||||
: ((Message) destination.getField(field)).toBuilder();
|
||||
merge(entry.getValue(), (Message) source.getField(field), childBuilder, options);
|
||||
destination.setField(field, childBuilder.buildPartial());
|
||||
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()) {
|
||||
if (!source.hasField(field)) {
|
||||
destination.clearField(field);
|
||||
} else {
|
||||
destination.setField(field, source.getField(field));
|
||||
}
|
||||
} else {
|
||||
if (source.hasField(field)) {
|
||||
destination.setField(
|
||||
field,
|
||||
((Message) destination.getField(field))
|
||||
.toBuilder().mergeFrom((Message) source.getField(field)).build());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (source.hasField(field) || !options.replacePrimitiveFields()) {
|
||||
destination.setField(field, source.getField(field));
|
||||
} else {
|
||||
destination.clearField(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
410
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
vendored
Normal file
410
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java
vendored
Normal file
@ -0,0 +1,410 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Utility helper functions to work with {@link com.google.protobuf.FieldMask}.
|
||||
*/
|
||||
public final 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) {
|
||||
// TODO(xiaofeng): Consider using com.google.common.base.Joiner here instead.
|
||||
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) {
|
||||
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
|
||||
return fromStringList(Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses from a string to a FieldMask and validates all field paths.
|
||||
*
|
||||
* @throws IllegalArgumentException if any of the field path is invalid.
|
||||
*/
|
||||
public static FieldMask fromString(Class<? extends Message> type, String value) {
|
||||
// TODO(xiaofeng): Consider using com.google.common.base.Splitter here instead.
|
||||
return fromStringList(type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a FieldMask for a list of field paths in a certain type.
|
||||
*
|
||||
* @throws IllegalArgumentException if any of the field path is not valid.
|
||||
*/
|
||||
// TODO(xiaofeng): Consider renaming fromStrings()
|
||||
public static FieldMask fromStringList(Class<? extends Message> type, Iterable<String> paths) {
|
||||
return fromStringList(Internal.getDefaultInstance(type).getDescriptorForType(), paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Descriptor descriptor, Iterable<String> paths) {
|
||||
return fromStringList(Optional.of(descriptor), paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a FieldMask for a list of field paths in a certain type. Does not validate the given
|
||||
* paths.
|
||||
*/
|
||||
public static FieldMask fromStringList(Iterable<String> paths) {
|
||||
return fromStringList(Optional.<Descriptor>absent(), paths);
|
||||
}
|
||||
|
||||
private static FieldMask fromStringList(Optional<Descriptor> descriptor, Iterable<String> paths) {
|
||||
FieldMask.Builder builder = FieldMask.newBuilder();
|
||||
for (String path : paths) {
|
||||
if (path.isEmpty()) {
|
||||
// Ignore empty field paths.
|
||||
continue;
|
||||
}
|
||||
if (descriptor.isPresent() && !isValid(descriptor.get(), path)) {
|
||||
throw new IllegalArgumentException(
|
||||
path + " is not a valid path for " + descriptor.get().getFullName());
|
||||
}
|
||||
builder.addPaths(path);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a FieldMask from the passed field numbers.
|
||||
*
|
||||
* @throws IllegalArgumentException if any of the fields are invalid for the message.
|
||||
*/
|
||||
public static FieldMask fromFieldNumbers(Class<? extends Message> type, int... fieldNumbers) {
|
||||
return fromFieldNumbers(type, Ints.asList(fieldNumbers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a FieldMask from the passed field numbers.
|
||||
*
|
||||
* @throws IllegalArgumentException if any of the fields are invalid for the message.
|
||||
*/
|
||||
public static FieldMask fromFieldNumbers(
|
||||
Class<? extends Message> type, Iterable<Integer> fieldNumbers) {
|
||||
Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
|
||||
|
||||
FieldMask.Builder builder = FieldMask.newBuilder();
|
||||
for (Integer fieldNumber : fieldNumbers) {
|
||||
FieldDescriptor field = descriptor.findFieldByNumber(fieldNumber);
|
||||
checkArgument(
|
||||
field != null,
|
||||
String.format("%s is not a valid field number for %s.", fieldNumber, type));
|
||||
builder.addPaths(field.getName());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a field mask to a Proto3 JSON string, that is converting from snake case to camel
|
||||
* case and joining all paths into one string with commas.
|
||||
*/
|
||||
public static String toJsonString(FieldMask fieldMask) {
|
||||
List<String> paths = new ArrayList<String>(fieldMask.getPathsCount());
|
||||
for (String path : fieldMask.getPathsList()) {
|
||||
if (path.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
paths.add(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, path));
|
||||
}
|
||||
return Joiner.on(FIELD_PATH_SEPARATOR).join(paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a field mask from a Proto3 JSON string, that is splitting the paths along commas and
|
||||
* converting from camel case to snake case.
|
||||
*/
|
||||
public static FieldMask fromJsonString(String value) {
|
||||
Iterable<String> paths = Splitter.on(FIELD_PATH_SEPARATOR).split(value);
|
||||
FieldMask.Builder builder = FieldMask.newBuilder();
|
||||
for (String path : paths) {
|
||||
if (path.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
builder.addPaths(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, path));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether paths in a given fields mask are valid.
|
||||
*/
|
||||
public static boolean isValid(Class<? extends Message> type, FieldMask fieldMask) {
|
||||
Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
|
||||
|
||||
return isValid(descriptor, fieldMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether paths in a given fields mask are valid.
|
||||
*/
|
||||
public static boolean isValid(Descriptor descriptor, FieldMask fieldMask) {
|
||||
for (String path : fieldMask.getPathsList()) {
|
||||
if (!isValid(descriptor, path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given field path is valid.
|
||||
*/
|
||||
public static boolean isValid(Class<? extends Message> type, String path) {
|
||||
Descriptor descriptor = Internal.getDefaultInstance(type).getDescriptorForType();
|
||||
|
||||
return isValid(descriptor, path);
|
||||
}
|
||||
|
||||
/** Checks whether paths in a given fields mask are valid. */
|
||||
public static boolean isValid(@Nullable Descriptor descriptor, String path) {
|
||||
String[] parts = path.split(FIELD_SEPARATOR_REGEX);
|
||||
if (parts.length == 0) {
|
||||
return false;
|
||||
}
|
||||
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 removed.
|
||||
*/
|
||||
public static FieldMask normalize(FieldMask mask) {
|
||||
return new FieldMaskTree(mask).toFieldMask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a union of two or more FieldMasks.
|
||||
*/
|
||||
public static FieldMask union(
|
||||
FieldMask firstMask, FieldMask secondMask, FieldMask... otherMasks) {
|
||||
FieldMaskTree maskTree = new FieldMaskTree(firstMask).mergeFromFieldMask(secondMask);
|
||||
for (FieldMask mask : otherMasks) {
|
||||
maskTree.mergeFromFieldMask(mask);
|
||||
}
|
||||
return maskTree.toFieldMask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts {@code secondMask} and {@code otherMasks} from {@code firstMask}.
|
||||
*
|
||||
* <p>This method disregards proto structure. That is, if {@code firstMask} is "foo" and {@code
|
||||
* secondMask} is "foo.bar", the response will always be "foo" without considering the internal
|
||||
* proto structure of message "foo".
|
||||
*/
|
||||
public static FieldMask subtract(
|
||||
FieldMask firstMask, FieldMask secondMask, FieldMask... otherMasks) {
|
||||
FieldMaskTree maskTree = new FieldMaskTree(firstMask).removeFromFieldMask(secondMask);
|
||||
for (FieldMask mask : otherMasks) {
|
||||
maskTree.removeFromFieldMask(mask);
|
||||
}
|
||||
return maskTree.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 final class MergeOptions {
|
||||
private boolean replaceMessageFields = false;
|
||||
private boolean replaceRepeatedFields = false;
|
||||
// TODO(b/28277137): change the default behavior to always replace primitive fields after
|
||||
// fixing all failing TAP tests.
|
||||
private boolean replacePrimitiveFields = false;
|
||||
|
||||
/**
|
||||
* Whether to replace message fields (i.e., discard existing content in
|
||||
* destination message fields).
|
||||
*/
|
||||
public boolean replaceMessageFields() {
|
||||
return replaceMessageFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to replace repeated fields (i.e., discard existing content in
|
||||
* destination repeated fields).
|
||||
*/
|
||||
public boolean replaceRepeatedFields() {
|
||||
return replaceRepeatedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to replace primitive (non-repeated and non-message) fields in
|
||||
* destination message fields with the source primitive fields (i.e., clear
|
||||
* destination field if source field is not set).
|
||||
*/
|
||||
public boolean replacePrimitiveFields() {
|
||||
return replacePrimitiveFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to replace message fields. Defaults to false.
|
||||
*
|
||||
* <p>If true, discard existing content in destination message fields when merging.
|
||||
*
|
||||
* <p>If false, merge the source message field into the destination message field.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public MergeOptions setReplaceMessageFields(boolean value) {
|
||||
replaceMessageFields = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to replace repeated fields. Defaults to false.
|
||||
*
|
||||
* <p>If true, discard existing content in destination repeated fields) when merging.
|
||||
*
|
||||
* <p>If false, append elements from source repeated field to the destination repeated field.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public MergeOptions setReplaceRepeatedFields(boolean value) {
|
||||
replaceRepeatedFields = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to replace primitive (non-repeated and non-message) fields in destination
|
||||
* message fields with the source primitive fields. Defaults to false.
|
||||
*
|
||||
* <p>If true, set the value of the destination primitive field to the source primitive field if
|
||||
* the source field is set, but clear the destination field otherwise.
|
||||
*
|
||||
* <p>If false, always set the value of the destination primitive field to the source primitive
|
||||
* field, and if the source field is unset, the default value of the source field is copied to
|
||||
* the destination.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public MergeOptions setReplacePrimitiveFields(boolean value) {
|
||||
replacePrimitiveFields = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges fields specified by a FieldMask from one message to another with the specified merge
|
||||
* options. The destination will remain unchanged if an empty FieldMask is provided.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of keeping only the masked fields of the given proto.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <P extends Message> P trim(FieldMask mask, P source) {
|
||||
Message.Builder destination = source.newBuilderForType();
|
||||
merge(mask, source, destination);
|
||||
return (P) destination.build();
|
||||
}
|
||||
}
|
2020
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
vendored
Normal file
2020
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
vendored
Normal file
File diff suppressed because it is too large
Load Diff
65
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Structs.java
vendored
Normal file
65
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Structs.java
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// 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.Struct;
|
||||
import com.google.protobuf.Value;
|
||||
|
||||
/** Utilities to help create {@code google.protobuf.Struct} messages. */
|
||||
public final class Structs {
|
||||
|
||||
/**
|
||||
* Returns a struct containing the key-value pair.
|
||||
*/
|
||||
public static Struct of(String k1, Value v1) {
|
||||
return Struct.newBuilder().putFields(k1, v1).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a struct containing each of the key-value pairs.
|
||||
*
|
||||
* <p>Providing duplicate keys is undefined behavior.
|
||||
*/
|
||||
public static Struct of(String k1, Value v1, String k2, Value v2) {
|
||||
return Struct.newBuilder().putFields(k1, v1).putFields(k2, v2).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a struct containing each of the key-value pairs.
|
||||
*
|
||||
* <p>Providing duplicate keys is undefined behavior.
|
||||
*/
|
||||
public static Struct of(String k1, Value v1, String k2, Value v2, String k3, Value v3) {
|
||||
return Struct.newBuilder().putFields(k1, v1).putFields(k2, v2).putFields(k3, v3).build();
|
||||
}
|
||||
|
||||
private Structs() {}
|
||||
}
|
484
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
vendored
Normal file
484
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
vendored
Normal file
@ -0,0 +1,484 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.math.IntMath.checkedAdd;
|
||||
import static com.google.common.math.IntMath.checkedSubtract;
|
||||
import static com.google.common.math.LongMath.checkedAdd;
|
||||
import static com.google.common.math.LongMath.checkedMultiply;
|
||||
import static com.google.common.math.LongMath.checkedSubtract;
|
||||
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.annotations.CompileTimeConstant;
|
||||
import com.google.j2objc.annotations.J2ObjCIncompatible;
|
||||
import com.google.protobuf.Duration;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import java.io.Serializable;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Utilities to help create/manipulate {@code protobuf/timestamp.proto}. All operations throw an
|
||||
* {@link IllegalArgumentException} if the input(s) are not {@linkplain #isValid(Timestamp) valid}.
|
||||
*/
|
||||
public final class Timestamps {
|
||||
|
||||
// Timestamp for "0001-01-01T00:00:00Z"
|
||||
static final long TIMESTAMP_SECONDS_MIN = -62135596800L;
|
||||
|
||||
// Timestamp for "9999-12-31T23:59:59Z"
|
||||
static final long TIMESTAMP_SECONDS_MAX = 253402300799L;
|
||||
|
||||
static final int NANOS_PER_SECOND = 1000000000;
|
||||
static final int NANOS_PER_MILLISECOND = 1000000;
|
||||
static final int NANOS_PER_MICROSECOND = 1000;
|
||||
static final int MILLIS_PER_SECOND = 1000;
|
||||
static final int MICROS_PER_SECOND = 1000000;
|
||||
|
||||
/** A constant holding the minimum valid {@link Timestamp}, {@code 0001-01-01T00:00:00Z}. */
|
||||
public static final Timestamp MIN_VALUE =
|
||||
Timestamp.newBuilder().setSeconds(TIMESTAMP_SECONDS_MIN).setNanos(0).build();
|
||||
|
||||
/**
|
||||
* A constant holding the maximum valid {@link Timestamp}, {@code 9999-12-31T23:59:59.999999999Z}.
|
||||
*/
|
||||
public static final Timestamp MAX_VALUE =
|
||||
Timestamp.newBuilder().setSeconds(TIMESTAMP_SECONDS_MAX).setNanos(999999999).build();
|
||||
|
||||
/**
|
||||
* A constant holding the {@link Timestamp} of epoch time, {@code 1970-01-01T00:00:00.000000000Z}.
|
||||
*/
|
||||
public static final Timestamp EPOCH = Timestamp.newBuilder().setSeconds(0).setNanos(0).build();
|
||||
|
||||
private static final ThreadLocal<SimpleDateFormat> timestampFormat =
|
||||
new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return createTimestampFormat();
|
||||
}
|
||||
};
|
||||
|
||||
private static SimpleDateFormat createTimestampFormat() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
|
||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||
// We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends
|
||||
// backwards to year one) for timestamp formatting.
|
||||
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
|
||||
sdf.setCalendar(calendar);
|
||||
return sdf;
|
||||
}
|
||||
|
||||
private Timestamps() {}
|
||||
|
||||
private static enum TimestampComparator implements Comparator<Timestamp>, Serializable {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public int compare(Timestamp t1, Timestamp t2) {
|
||||
checkValid(t1);
|
||||
checkValid(t2);
|
||||
int secDiff = Long.compare(t1.getSeconds(), t2.getSeconds());
|
||||
return (secDiff != 0) ? secDiff : Integer.compare(t1.getNanos(), t2.getNanos());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Comparator} for {@link Timestamp Timestamps} which sorts in increasing
|
||||
* chronological order. Nulls and invalid {@link Timestamp Timestamps} are not allowed (see
|
||||
* {@link #isValid}). The returned comparator is serializable.
|
||||
*/
|
||||
public static Comparator<Timestamp> comparator() {
|
||||
return TimestampComparator.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two timestamps. The value returned is identical to what would be returned by: {@code
|
||||
* Timestamps.comparator().compare(x, y)}.
|
||||
*
|
||||
* @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if {@code x < y};
|
||||
* and a value greater than {@code 0} if {@code x > y}
|
||||
*/
|
||||
public static int compare(Timestamp x, Timestamp y) {
|
||||
return TimestampComparator.INSTANCE.compare(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the
|
||||
* range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and
|
||||
* 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range [0, +999,999,999].
|
||||
*
|
||||
* <p><b>Note:</b> Negative second values with fractional seconds must still have non-negative
|
||||
* nanos values that count forward in time.
|
||||
*/
|
||||
public static boolean isValid(Timestamp timestamp) {
|
||||
return isValid(timestamp.getSeconds(), timestamp.getNanos());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The {@code
|
||||
* seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between
|
||||
* 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range
|
||||
* [0, +999,999,999].
|
||||
*
|
||||
* <p><b>Note:</b> Negative second values with fractional seconds must still have non-negative
|
||||
* nanos values that count forward in time.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static boolean isValid(long seconds, int nanos) {
|
||||
if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) {
|
||||
return false;
|
||||
}
|
||||
if (nanos < 0 || nanos >= NANOS_PER_SECOND) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Throws an {@link IllegalArgumentException} if the given {@link Timestamp} is not valid. */
|
||||
@CanIgnoreReturnValue
|
||||
public static Timestamp checkValid(Timestamp timestamp) {
|
||||
long seconds = timestamp.getSeconds();
|
||||
int nanos = timestamp.getNanos();
|
||||
if (!isValid(seconds, nanos)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Timestamp is not valid. See proto definition for valid values. "
|
||||
+ "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. "
|
||||
+ "Nanos (%s) must be in range [0, +999,999,999].",
|
||||
seconds, nanos));
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the given builder and throws an {@link IllegalArgumentException} if it is not valid. See
|
||||
* {@link #checkValid(Timestamp)}.
|
||||
*
|
||||
* @return A valid, built {@link Timestamp}.
|
||||
*/
|
||||
public static Timestamp checkValid(Timestamp.Builder timestampBuilder) {
|
||||
return checkValid(timestampBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
checkValid(timestamp);
|
||||
|
||||
long seconds = timestamp.getSeconds();
|
||||
int nanos = timestamp.getNanos();
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
// Format the seconds part.
|
||||
Date date = new Date(seconds * MILLIS_PER_SECOND);
|
||||
result.append(timestampFormat.get().format(date));
|
||||
// Format the nanos part.
|
||||
if (nanos != 0) {
|
||||
result.append(".");
|
||||
result.append(formatNanos(nanos));
|
||||
}
|
||||
result.append("Z");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse from RFC 3339 date string to Timestamp. This method accepts all outputs of {@link
|
||||
* #toString(Timestamp)} and it also accepts any fractional digits (or none) and any offset as
|
||||
* long as they fit into nano-seconds precision.
|
||||
*
|
||||
* <p>Example of accepted format: "1972-01-01T10:00:20.021-05:00"
|
||||
*
|
||||
* @return a Timestamp parsed from the string
|
||||
* @throws ParseException if parsing fails
|
||||
*/
|
||||
public static Timestamp parse(String value) throws ParseException {
|
||||
int dayOffset = value.indexOf('T');
|
||||
if (dayOffset == -1) {
|
||||
throw new ParseException("Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0);
|
||||
}
|
||||
int timezoneOffsetPosition = value.indexOf('Z', dayOffset);
|
||||
if (timezoneOffsetPosition == -1) {
|
||||
timezoneOffsetPosition = value.indexOf('+', dayOffset);
|
||||
}
|
||||
if (timezoneOffsetPosition == -1) {
|
||||
timezoneOffsetPosition = value.indexOf('-', dayOffset);
|
||||
}
|
||||
if (timezoneOffsetPosition == -1) {
|
||||
throw new ParseException("Failed to parse timestamp: missing valid timezone offset.", 0);
|
||||
}
|
||||
// Parse seconds and nanos.
|
||||
String timeValue = value.substring(0, timezoneOffsetPosition);
|
||||
String secondValue = timeValue;
|
||||
String nanoValue = "";
|
||||
int pointPosition = timeValue.indexOf('.');
|
||||
if (pointPosition != -1) {
|
||||
secondValue = timeValue.substring(0, pointPosition);
|
||||
nanoValue = timeValue.substring(pointPosition + 1);
|
||||
}
|
||||
Date date = timestampFormat.get().parse(secondValue);
|
||||
long seconds = date.getTime() / MILLIS_PER_SECOND;
|
||||
int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue);
|
||||
// Parse timezone offsets.
|
||||
if (value.charAt(timezoneOffsetPosition) == 'Z') {
|
||||
if (value.length() != timezoneOffsetPosition + 1) {
|
||||
throw new ParseException(
|
||||
"Failed to parse timestamp: invalid trailing data \""
|
||||
+ value.substring(timezoneOffsetPosition)
|
||||
+ "\"",
|
||||
0);
|
||||
}
|
||||
} else {
|
||||
String offsetValue = value.substring(timezoneOffsetPosition + 1);
|
||||
long offset = parseTimezoneOffset(offsetValue);
|
||||
if (value.charAt(timezoneOffsetPosition) == '+') {
|
||||
seconds -= offset;
|
||||
} else {
|
||||
seconds += offset;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return normalizedTimestamp(seconds, nanos);
|
||||
} catch (IllegalArgumentException e) {
|
||||
ParseException ex = new ParseException(
|
||||
"Failed to parse timestamp " + value + " Timestamp is out of range.", 0);
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string in RFC 3339 format into a {@link Timestamp}.
|
||||
*
|
||||
* <p>Identical to {@link #parse(String)}, but throws an {@link IllegalArgumentException} instead
|
||||
* of a {@link ParseException} if parsing fails.
|
||||
*
|
||||
* @return a {@link Timestamp} parsed from the string
|
||||
* @throws IllegalArgumentException if parsing fails
|
||||
*/
|
||||
public static Timestamp parseUnchecked(@CompileTimeConstant String value) {
|
||||
try {
|
||||
return parse(value);
|
||||
} catch (ParseException e) {
|
||||
// While `java.time.format.DateTimeParseException` is a more accurate representation of the
|
||||
// failure, this library is currently not JDK8 ready because of Android dependencies.
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a Timestamp from the number of seconds elapsed from the epoch. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Timestamp fromSeconds(long seconds) {
|
||||
return normalizedTimestamp(seconds, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Timestamp to the number of seconds elapsed from the epoch.
|
||||
*
|
||||
* <p>The result will be rounded down to the nearest second. E.g., if the timestamp represents
|
||||
* "1969-12-31T23:59:59.999999999Z", it will be rounded to -1 second.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toSeconds(Timestamp timestamp) {
|
||||
return checkValid(timestamp).getSeconds();
|
||||
}
|
||||
|
||||
/** Create a Timestamp from the number of milliseconds elapsed from the epoch. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Timestamp fromMillis(long milliseconds) {
|
||||
return normalizedTimestamp(
|
||||
milliseconds / MILLIS_PER_SECOND,
|
||||
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Timestamp from a java.util.Date. If the java.util.Date is a java.sql.Timestamp,
|
||||
* full nanonsecond precision is retained.
|
||||
*
|
||||
* @throws IllegalArgumentException if the year is before 1 CE or after 9999 CE
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
@J2ObjCIncompatible
|
||||
public static Timestamp fromDate(Date date) {
|
||||
if (date instanceof java.sql.Timestamp) {
|
||||
java.sql.Timestamp sqlTimestamp = (java.sql.Timestamp) date;
|
||||
long integralSeconds = sqlTimestamp.getTime() / 1000L; // truncate the fractional seconds
|
||||
return Timestamp.newBuilder()
|
||||
.setSeconds(integralSeconds)
|
||||
.setNanos(sqlTimestamp.getNanos())
|
||||
.build();
|
||||
} else {
|
||||
return fromMillis(date.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Timestamp to the number of milliseconds elapsed from the epoch.
|
||||
*
|
||||
* <p>The result will be rounded down to the nearest millisecond. For instance, if the timestamp
|
||||
* represents "1969-12-31T23:59:59.999999999Z", it will be rounded to -1 millisecond.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toMillis(Timestamp timestamp) {
|
||||
checkValid(timestamp);
|
||||
return checkedAdd(
|
||||
checkedMultiply(timestamp.getSeconds(), MILLIS_PER_SECOND),
|
||||
timestamp.getNanos() / NANOS_PER_MILLISECOND);
|
||||
}
|
||||
|
||||
/** Create a Timestamp from the number of microseconds elapsed from the epoch. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Timestamp fromMicros(long microseconds) {
|
||||
return normalizedTimestamp(
|
||||
microseconds / MICROS_PER_SECOND,
|
||||
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Timestamp to the number of microseconds elapsed from the epoch.
|
||||
*
|
||||
* <p>The result will be rounded down to the nearest microsecond. E.g., if the timestamp
|
||||
* represents "1969-12-31T23:59:59.999999999Z", it will be rounded to -1 microsecond.
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toMicros(Timestamp timestamp) {
|
||||
checkValid(timestamp);
|
||||
return checkedAdd(
|
||||
checkedMultiply(timestamp.getSeconds(), MICROS_PER_SECOND),
|
||||
timestamp.getNanos() / NANOS_PER_MICROSECOND);
|
||||
}
|
||||
|
||||
/** Create a Timestamp from the number of nanoseconds elapsed from the epoch. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static Timestamp fromNanos(long nanoseconds) {
|
||||
return normalizedTimestamp(
|
||||
nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND));
|
||||
}
|
||||
|
||||
/** Convert a Timestamp to the number of nanoseconds elapsed from the epoch. */
|
||||
@SuppressWarnings("GoodTime") // this is a legacy conversion API
|
||||
public static long toNanos(Timestamp timestamp) {
|
||||
checkValid(timestamp);
|
||||
return checkedAdd(
|
||||
checkedMultiply(timestamp.getSeconds(), NANOS_PER_SECOND), timestamp.getNanos());
|
||||
}
|
||||
|
||||
/** Calculate the difference between two timestamps. */
|
||||
public static Duration between(Timestamp from, Timestamp to) {
|
||||
checkValid(from);
|
||||
checkValid(to);
|
||||
return Durations.normalizedDuration(
|
||||
checkedSubtract(to.getSeconds(), from.getSeconds()),
|
||||
checkedSubtract(to.getNanos(), from.getNanos()));
|
||||
}
|
||||
|
||||
/** Add a duration to a timestamp. */
|
||||
public static Timestamp add(Timestamp start, Duration length) {
|
||||
checkValid(start);
|
||||
Durations.checkValid(length);
|
||||
return normalizedTimestamp(
|
||||
checkedAdd(start.getSeconds(), length.getSeconds()),
|
||||
checkedAdd(start.getNanos(), length.getNanos()));
|
||||
}
|
||||
|
||||
/** Subtract a duration from a timestamp. */
|
||||
public static Timestamp subtract(Timestamp start, Duration length) {
|
||||
checkValid(start);
|
||||
Durations.checkValid(length);
|
||||
return normalizedTimestamp(
|
||||
checkedSubtract(start.getSeconds(), length.getSeconds()),
|
||||
checkedSubtract(start.getNanos(), length.getNanos()));
|
||||
}
|
||||
|
||||
static Timestamp normalizedTimestamp(long seconds, int nanos) {
|
||||
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) {
|
||||
seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND);
|
||||
nanos = (int) (nanos % NANOS_PER_SECOND);
|
||||
}
|
||||
if (nanos < 0) {
|
||||
nanos =
|
||||
(int)
|
||||
(nanos + NANOS_PER_SECOND); // no overflow since nanos is negative (and we're adding)
|
||||
seconds = checkedSubtract(seconds, 1);
|
||||
}
|
||||
Timestamp timestamp = Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
|
||||
return checkValid(timestamp);
|
||||
}
|
||||
|
||||
private static long parseTimezoneOffset(String value) throws ParseException {
|
||||
int pos = value.indexOf(':');
|
||||
if (pos == -1) {
|
||||
throw new ParseException("Invalid offset value: " + value, 0);
|
||||
}
|
||||
String hours = value.substring(0, pos);
|
||||
String minutes = value.substring(pos + 1);
|
||||
return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60;
|
||||
}
|
||||
|
||||
static int parseNanos(String value) throws ParseException {
|
||||
int result = 0;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
result = result * 10;
|
||||
if (i < value.length()) {
|
||||
if (value.charAt(i) < '0' || value.charAt(i) > '9') {
|
||||
throw new ParseException("Invalid nanoseconds.", 0);
|
||||
}
|
||||
result += value.charAt(i) - '0';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Format the nano part of a timestamp or a duration. */
|
||||
static String formatNanos(int nanos) {
|
||||
// Determine whether to use 3, 6, or 9 digits for the nano part.
|
||||
if (nanos % NANOS_PER_MILLISECOND == 0) {
|
||||
return String.format(Locale.ENGLISH, "%1$03d", nanos / NANOS_PER_MILLISECOND);
|
||||
} else if (nanos % NANOS_PER_MICROSECOND == 0) {
|
||||
return String.format(Locale.ENGLISH, "%1$06d", nanos / NANOS_PER_MICROSECOND);
|
||||
} else {
|
||||
return String.format(Locale.ENGLISH, "%1$09d", nanos);
|
||||
}
|
||||
}
|
||||
}
|
85
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Values.java
vendored
Normal file
85
deps/protobuf/java/util/src/main/java/com/google/protobuf/util/Values.java
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
// 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.ListValue;
|
||||
import com.google.protobuf.NullValue;
|
||||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.Value;
|
||||
|
||||
/** Utilities to help create {@code google.protobuf.Value} messages. */
|
||||
public final class Values {
|
||||
|
||||
private static final Value NULL_VALUE =
|
||||
Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build();
|
||||
|
||||
public static Value ofNull() {
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
/** Returns a Value object with number set to value. */
|
||||
public static Value of(boolean value) {
|
||||
return Value.newBuilder().setBoolValue(value).build();
|
||||
}
|
||||
|
||||
/** Returns a Value object with number set to value. */
|
||||
public static Value of(double value) {
|
||||
return Value.newBuilder().setNumberValue(value).build();
|
||||
}
|
||||
|
||||
/** Returns a Value object with string set to value. */
|
||||
public static Value of(String value) {
|
||||
return Value.newBuilder().setStringValue(value).build();
|
||||
}
|
||||
|
||||
/** Returns a Value object with struct set to value. */
|
||||
public static Value of(Struct value) {
|
||||
return Value.newBuilder().setStructValue(value).build();
|
||||
}
|
||||
|
||||
/** Returns a Value with ListValue set to value. */
|
||||
public static Value of(ListValue value) {
|
||||
return Value.newBuilder().setListValue(value).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Value with ListValue set to the appending the result of calling {@link #of} on each
|
||||
* element in the iterable.
|
||||
*/
|
||||
public static Value of(Iterable<Value> values) {
|
||||
Value.Builder valueBuilder = Value.newBuilder();
|
||||
ListValue.Builder listValue = valueBuilder.getListValueBuilder();
|
||||
listValue.addAllValues(values);
|
||||
return valueBuilder.build();
|
||||
}
|
||||
|
||||
private Values() {}
|
||||
}
|
631
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/DurationsTest.java
vendored
Normal file
631
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/DurationsTest.java
vendored
Normal file
@ -0,0 +1,631 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static com.google.protobuf.util.Durations.toSecondsAsDouble;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.protobuf.Duration;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link Durations}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class DurationsTest {
|
||||
|
||||
private static final Duration INVALID_MAX =
|
||||
Duration.newBuilder().setSeconds(Long.MAX_VALUE).setNanos(Integer.MAX_VALUE).build();
|
||||
private static final Duration INVALID_MIN =
|
||||
Duration.newBuilder().setSeconds(Long.MIN_VALUE).setNanos(Integer.MIN_VALUE).build();
|
||||
|
||||
@Test
|
||||
public void testIsPositive() {
|
||||
assertThat(Durations.isPositive(Durations.ZERO)).isFalse();
|
||||
assertThat(Durations.isPositive(Durations.fromNanos(-1))).isFalse();
|
||||
assertThat(Durations.isPositive(Durations.fromNanos(1))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNegative() {
|
||||
assertThat(Durations.isNegative(Durations.ZERO)).isFalse();
|
||||
assertThat(Durations.isNegative(Durations.fromNanos(1))).isFalse();
|
||||
assertThat(Durations.isNegative(Durations.fromNanos(-1))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckNotNegative() {
|
||||
Durations.checkNotNegative(Durations.ZERO);
|
||||
Durations.checkNotNegative(Durations.fromNanos(1));
|
||||
Durations.checkNotNegative(Durations.fromSeconds(1));
|
||||
|
||||
try {
|
||||
Durations.checkNotNegative(Durations.fromNanos(-1));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo(
|
||||
"duration (-0.000000001s) must not be negative");
|
||||
}
|
||||
try {
|
||||
Durations.checkNotNegative(Durations.fromSeconds(-1));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("duration (-1s) must not be negative");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckPositive() {
|
||||
Durations.checkPositive(Durations.fromNanos(1));
|
||||
Durations.checkPositive(Durations.fromSeconds(1));
|
||||
|
||||
try {
|
||||
Durations.checkPositive(Durations.ZERO);
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("duration (0s) must be positive");
|
||||
}
|
||||
|
||||
try {
|
||||
Durations.checkPositive(Durations.fromNanos(-1));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("duration (-0.000000001s) must be positive");
|
||||
}
|
||||
|
||||
try {
|
||||
Durations.checkPositive(Durations.fromSeconds(-1));
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("duration (-1s) must be positive");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToSecondsAsDouble() {
|
||||
assertThat(toSecondsAsDouble(duration(0, 1))).isEqualTo(1.0e-9);
|
||||
assertThat(toSecondsAsDouble(Durations.fromMillis(999))).isEqualTo(0.999);
|
||||
assertThat(toSecondsAsDouble(duration(1, 0))).isEqualTo(1.0);
|
||||
assertWithMessage("Precision loss detected")
|
||||
.that(toSecondsAsDouble(Durations.fromNanos(1234567890987654321L)))
|
||||
.isWithin(1.0e-6)
|
||||
.of(1234567890.9876544);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundtripConversions() {
|
||||
for (long value : Arrays.asList(-123456, -42, -1, 0, 1, 42, 123456)) {
|
||||
assertThat(Durations.toDays(Durations.fromDays(value))).isEqualTo(value);
|
||||
assertThat(Durations.toHours(Durations.fromHours(value))).isEqualTo(value);
|
||||
assertThat(Durations.toMinutes(Durations.fromMinutes(value))).isEqualTo(value);
|
||||
assertThat(Durations.toSeconds(Durations.fromSeconds(value))).isEqualTo(value);
|
||||
assertThat(Durations.toMillis(Durations.fromMillis(value))).isEqualTo(value);
|
||||
assertThat(Durations.toMicros(Durations.fromMicros(value))).isEqualTo(value);
|
||||
assertThat(Durations.toNanos(Durations.fromNanos(value))).isEqualTo(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticFactories() {
|
||||
Duration[] durations = {
|
||||
Durations.fromDays(1),
|
||||
Durations.fromHours(24),
|
||||
Durations.fromMinutes(1440),
|
||||
Durations.fromSeconds(86_400L),
|
||||
Durations.fromMillis(86_400_000L),
|
||||
Durations.fromMicros(86_400_000_000L),
|
||||
Durations.fromNanos(86_400_000_000_000L)
|
||||
};
|
||||
|
||||
for (Duration d1 : durations) {
|
||||
assertThat(d1).isNotEqualTo(null);
|
||||
for (Duration d2 : durations) {
|
||||
assertThat(d1).isEqualTo(d2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinMaxAreValid() {
|
||||
assertThat(Durations.isValid(Durations.MAX_VALUE)).isTrue();
|
||||
assertThat(Durations.isValid(Durations.MIN_VALUE)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValid_false() {
|
||||
assertThat(Durations.isValid(-315576000001L, 0)).isFalse();
|
||||
assertThat(Durations.isValid(315576000001L, 0)).isFalse();
|
||||
assertThat(Durations.isValid(42L, -42)).isFalse();
|
||||
assertThat(Durations.isValid(-42L, 42)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValid_true() {
|
||||
assertThat(Durations.isValid(0L, 42)).isTrue();
|
||||
assertThat(Durations.isValid(0L, -42)).isTrue();
|
||||
assertThat(Durations.isValid(42L, 0)).isTrue();
|
||||
assertThat(Durations.isValid(42L, 42)).isTrue();
|
||||
assertThat(Durations.isValid(-315576000000L, 0)).isTrue();
|
||||
assertThat(Durations.isValid(315576000000L, 0)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_outOfRange() throws ParseException {
|
||||
try {
|
||||
Duration x = Durations.parse("316576000000.123456789123456789s");
|
||||
fail("expected ParseException");
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Duration value is out of range.");
|
||||
assertThat(expected).hasCauseThat().isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDurationStringFormat() throws Exception {
|
||||
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
|
||||
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
|
||||
Duration duration = Timestamps.between(start, end);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s");
|
||||
duration = Timestamps.between(end, start);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s");
|
||||
|
||||
// Generated output should contain 3, 6, or 9 fractional digits.
|
||||
duration = Duration.newBuilder().setSeconds(1).build();
|
||||
assertThat(Durations.toString(duration)).isEqualTo("1s");
|
||||
duration = Duration.newBuilder().setNanos(10000000).build();
|
||||
assertThat(Durations.toString(duration)).isEqualTo("0.010s");
|
||||
duration = Duration.newBuilder().setNanos(10000).build();
|
||||
assertThat(Durations.toString(duration)).isEqualTo("0.000010s");
|
||||
duration = Duration.newBuilder().setNanos(10).build();
|
||||
assertThat(Durations.toString(duration)).isEqualTo("0.000000010s");
|
||||
|
||||
// Parsing accepts an fractional digits as long as they fit into nano
|
||||
// precision.
|
||||
duration = Durations.parse("0.1s");
|
||||
assertThat(duration.getNanos()).isEqualTo(100000000);
|
||||
duration = Durations.parse("0.0001s");
|
||||
assertThat(duration.getNanos()).isEqualTo(100000);
|
||||
duration = Durations.parse("0.0000001s");
|
||||
assertThat(duration.getNanos()).isEqualTo(100);
|
||||
// Repeat using parseUnchecked().
|
||||
duration = Durations.parseUnchecked("0.1s");
|
||||
assertThat(duration.getNanos()).isEqualTo(100000000);
|
||||
duration = Durations.parseUnchecked("0.0001s");
|
||||
assertThat(duration.getNanos()).isEqualTo(100000);
|
||||
duration = Durations.parseUnchecked("0.0000001s");
|
||||
assertThat(duration.getNanos()).isEqualTo(100);
|
||||
|
||||
// Duration must support range from -315,576,000,000s to +315576000000s
|
||||
// which includes negative values.
|
||||
duration = Durations.parse("315576000000.999999999s");
|
||||
assertThat(duration.getSeconds()).isEqualTo(315576000000L);
|
||||
assertThat(duration.getNanos()).isEqualTo(999999999);
|
||||
duration = Durations.parse("-315576000000.999999999s");
|
||||
assertThat(duration.getSeconds()).isEqualTo(-315576000000L);
|
||||
assertThat(duration.getNanos()).isEqualTo(-999999999);
|
||||
// Repeat using parseUnchecked().
|
||||
duration = Durations.parseUnchecked("315576000000.999999999s");
|
||||
assertThat(duration.getSeconds()).isEqualTo(315576000000L);
|
||||
assertThat(duration.getNanos()).isEqualTo(999999999);
|
||||
duration = Durations.parseUnchecked("-315576000000.999999999s");
|
||||
assertThat(duration.getSeconds()).isEqualTo(-315576000000L);
|
||||
assertThat(duration.getNanos()).isEqualTo(-999999999);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDurationInvalidFormat() {
|
||||
// Value too small.
|
||||
try {
|
||||
Durations.toString(
|
||||
Duration.newBuilder().setSeconds(Durations.DURATION_SECONDS_MIN - 1).build());
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
try {
|
||||
Durations.toString(
|
||||
Duration.newBuilder().setSeconds(Durations.DURATION_SECONDS_MAX + 1).build());
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Invalid nanos value.
|
||||
try {
|
||||
Durations.toString(Duration.newBuilder().setSeconds(1).setNanos(-1).build());
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Invalid seconds value.
|
||||
try {
|
||||
Durations.toString(Duration.newBuilder().setSeconds(-1).setNanos(1).build());
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Value too small.
|
||||
try {
|
||||
Durations.parse("-315576000001s");
|
||||
fail();
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Durations.parseUnchecked("-315576000001s");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Value too large.
|
||||
try {
|
||||
Durations.parse("315576000001s");
|
||||
fail();
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Durations.parseUnchecked("315576000001s");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Empty.
|
||||
try {
|
||||
Durations.parse("");
|
||||
fail();
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Durations.parseUnchecked("");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Missing "s".
|
||||
try {
|
||||
Durations.parse("0");
|
||||
fail();
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Durations.parseUnchecked("0");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Invalid trailing data.
|
||||
try {
|
||||
Durations.parse("0s0");
|
||||
fail();
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Durations.parseUnchecked("0s0");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
|
||||
// Invalid prefix.
|
||||
try {
|
||||
Durations.parse("--1s");
|
||||
fail();
|
||||
} catch (ParseException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Durations.parseUnchecked("--1s");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDurationConversion() throws Exception {
|
||||
Duration duration = Durations.parse("1.111111111s");
|
||||
assertThat(Durations.toNanos(duration)).isEqualTo(1111111111);
|
||||
assertThat(Durations.toMicros(duration)).isEqualTo(1111111);
|
||||
assertThat(Durations.toMillis(duration)).isEqualTo(1111);
|
||||
assertThat(Durations.toSeconds(duration)).isEqualTo(1);
|
||||
duration = Durations.fromNanos(1111111111);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("1.111111111s");
|
||||
duration = Durations.fromMicros(1111111);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("1.111111s");
|
||||
duration = Durations.fromMillis(1111);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("1.111s");
|
||||
duration = Durations.fromSeconds(1);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("1s");
|
||||
|
||||
duration = Durations.parse("-1.111111111s");
|
||||
assertThat(Durations.toNanos(duration)).isEqualTo(-1111111111);
|
||||
assertThat(Durations.toMicros(duration)).isEqualTo(-1111111);
|
||||
assertThat(Durations.toMillis(duration)).isEqualTo(-1111);
|
||||
assertThat(Durations.toSeconds(duration)).isEqualTo(-1);
|
||||
duration = Durations.fromNanos(-1111111111);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-1.111111111s");
|
||||
duration = Durations.fromMicros(-1111111);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-1.111111s");
|
||||
duration = Durations.fromMillis(-1111);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-1.111s");
|
||||
duration = Durations.fromSeconds(-1);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-1s");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeOperations() throws Exception {
|
||||
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
|
||||
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
|
||||
|
||||
Duration duration = Timestamps.between(start, end);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s");
|
||||
Timestamp value = Timestamps.add(start, duration);
|
||||
assertThat(value).isEqualTo(end);
|
||||
value = Timestamps.subtract(end, duration);
|
||||
assertThat(value).isEqualTo(start);
|
||||
|
||||
duration = Timestamps.between(end, start);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s");
|
||||
value = Timestamps.add(end, duration);
|
||||
assertThat(value).isEqualTo(start);
|
||||
value = Timestamps.subtract(start, duration);
|
||||
assertThat(value).isEqualTo(end);
|
||||
|
||||
duration = Durations.parse("-1.125s");
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-1.125s");
|
||||
|
||||
duration = Durations.add(duration, duration);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-2.250s");
|
||||
|
||||
duration = Durations.subtract(duration, Durations.parse("-1s"));
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-1.250s");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
assertThat(Durations.toString(duration(1, 1))).isEqualTo("1.000000001s");
|
||||
assertThat(Durations.toString(duration(-1, -1))).isEqualTo("-1.000000001s");
|
||||
assertThat(Durations.toString(duration(1, 0))).isEqualTo("1s");
|
||||
assertThat(Durations.toString(duration(-1, 0))).isEqualTo("-1s");
|
||||
assertThat(Durations.toString(duration(0, 1))).isEqualTo("0.000000001s");
|
||||
assertThat(Durations.toString(duration(0, -1))).isEqualTo("-0.000000001s");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
assertThat(Durations.add(duration(1, 10), duration(1, 20))).isEqualTo(duration(2, 30));
|
||||
assertThat(Durations.add(duration(1, 999999999), duration(1, 2))).isEqualTo(duration(3, 1));
|
||||
assertThat(Durations.add(duration(1, 999999999), duration(1, 1))).isEqualTo(duration(3, 0));
|
||||
assertThat(Durations.add(duration(1, 999999999), duration(-2, -1))).isEqualTo(duration(0, -2));
|
||||
assertThat(Durations.add(duration(1, 999999999), duration(-2, 0))).isEqualTo(duration(0, -1));
|
||||
assertThat(Durations.add(duration(-1, -10), duration(-1, -20))).isEqualTo(duration(-2, -30));
|
||||
assertThat(Durations.add(duration(-1, -999999999), duration(-1, -2)))
|
||||
.isEqualTo(duration(-3, -1));
|
||||
assertThat(Durations.add(duration(-1, -999999999), duration(-1, -1)))
|
||||
.isEqualTo(duration(-3, 0));
|
||||
assertThat(Durations.add(duration(-1, -999999999), duration(2, 1))).isEqualTo(duration(0, 2));
|
||||
assertThat(Durations.add(duration(-1, -999999999), duration(2, 0))).isEqualTo(duration(0, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
assertThat(Durations.subtract(duration(3, 2), duration(1, 1))).isEqualTo(duration(2, 1));
|
||||
assertThat(Durations.subtract(duration(3, 10), duration(1, 10))).isEqualTo(duration(2, 0));
|
||||
assertThat(Durations.subtract(duration(3, 1), duration(1, 2)))
|
||||
.isEqualTo(duration(1, 999999999));
|
||||
assertThat(Durations.subtract(duration(3, 2), duration(4, 1)))
|
||||
.isEqualTo(duration(0, -999999999));
|
||||
assertThat(Durations.subtract(duration(1, 1), duration(3, 2))).isEqualTo(duration(-2, -1));
|
||||
assertThat(Durations.subtract(duration(1, 10), duration(3, 10))).isEqualTo(duration(-2, 0));
|
||||
assertThat(Durations.subtract(duration(1, 2), duration(3, 1)))
|
||||
.isEqualTo(duration(-1, -999999999));
|
||||
assertThat(Durations.subtract(duration(4, 1), duration(3, 2)))
|
||||
.isEqualTo(duration(0, 999999999));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComparator() {
|
||||
assertThat(Durations.comparator().compare(duration(3, 2), duration(3, 2))).isEqualTo(0);
|
||||
assertThat(Durations.comparator().compare(duration(0, 0), duration(0, 0))).isEqualTo(0);
|
||||
assertThat(Durations.comparator().compare(duration(3, 1), duration(1, 1))).isGreaterThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(3, 2), duration(3, 1))).isGreaterThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(1, 1), duration(3, 1))).isLessThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(3, 1), duration(3, 2))).isLessThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(-3, -1), duration(-1, -1))).isLessThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(-3, -2), duration(-3, -1))).isLessThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(-1, -1), duration(-3, -1))).isGreaterThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(-3, -1), duration(-3, -2))).isGreaterThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(-10, -1), duration(1, 1))).isLessThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(0, -1), duration(0, 1))).isLessThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(0x80000000L, 0), duration(0, 0)))
|
||||
.isGreaterThan(0);
|
||||
assertThat(Durations.comparator().compare(duration(0xFFFFFFFF00000000L, 0), duration(0, 0)))
|
||||
.isLessThan(0);
|
||||
|
||||
Duration duration0 = duration(-50, -500);
|
||||
Duration duration1 = duration(-50, -400);
|
||||
Duration duration2 = duration(50, 500);
|
||||
Duration duration3 = duration(100, 20);
|
||||
Duration duration4 = duration(100, 50);
|
||||
Duration duration5 = duration(100, 150);
|
||||
Duration duration6 = duration(150, 40);
|
||||
|
||||
List<Duration> durations =
|
||||
Lists.newArrayList(
|
||||
duration5, duration3, duration1, duration4, duration6, duration2, duration0, duration3);
|
||||
|
||||
Collections.sort(durations, Durations.comparator());
|
||||
assertThat(durations)
|
||||
.containsExactly(
|
||||
duration0, duration1, duration2, duration3, duration3, duration4, duration5, duration6)
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompare() {
|
||||
assertThat(Durations.compare(duration(3, 2), duration(3, 2))).isEqualTo(0);
|
||||
assertThat(Durations.compare(duration(0, 0), duration(0, 0))).isEqualTo(0);
|
||||
assertThat(Durations.compare(duration(3, 1), duration(1, 1))).isGreaterThan(0);
|
||||
assertThat(Durations.compare(duration(3, 2), duration(3, 1))).isGreaterThan(0);
|
||||
assertThat(Durations.compare(duration(1, 1), duration(3, 1))).isLessThan(0);
|
||||
assertThat(Durations.compare(duration(3, 1), duration(3, 2))).isLessThan(0);
|
||||
assertThat(Durations.compare(duration(-3, -1), duration(-1, -1))).isLessThan(0);
|
||||
assertThat(Durations.compare(duration(-3, -2), duration(-3, -1))).isLessThan(0);
|
||||
assertThat(Durations.compare(duration(-1, -1), duration(-3, -1))).isGreaterThan(0);
|
||||
assertThat(Durations.compare(duration(-3, -1), duration(-3, -2))).isGreaterThan(0);
|
||||
assertThat(Durations.compare(duration(-10, -1), duration(1, 1))).isLessThan(0);
|
||||
assertThat(Durations.compare(duration(0, -1), duration(0, 1))).isLessThan(0);
|
||||
assertThat(Durations.compare(duration(0x80000000L, 0), duration(0, 0))).isGreaterThan(0);
|
||||
assertThat(Durations.compare(duration(0xFFFFFFFF00000000L, 0), duration(0, 0))).isLessThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverflows() throws Exception {
|
||||
try {
|
||||
Durations.toNanos(duration(315576000000L, 999999999));
|
||||
assertWithMessage("Expected an ArithmeticException to be thrown").fail();
|
||||
} catch (ArithmeticException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
Durations.add(Durations.MAX_VALUE, Durations.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.subtract(Durations.MIN_VALUE, Durations.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
Durations.toNanos(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.toMicros(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.toMillis(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.toSeconds(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
Durations.toNanos(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.toMicros(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.toMillis(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.toSeconds(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
|
||||
assertThat(Durations.toString(Durations.fromNanos(Long.MAX_VALUE)))
|
||||
.isEqualTo("9223372036.854775807s");
|
||||
try {
|
||||
Durations.fromMicros(Long.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.fromMillis(Long.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.fromSeconds(Long.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
|
||||
assertThat(Durations.toString(Durations.fromNanos(Long.MIN_VALUE)))
|
||||
.isEqualTo("-9223372036.854775808s");
|
||||
try {
|
||||
Durations.fromMicros(Long.MIN_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.fromMillis(Long.MIN_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
try {
|
||||
Durations.fromSeconds(Long.MIN_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
static Duration duration(long seconds, int nanos) {
|
||||
return Durations.checkValid(
|
||||
Durations.checkValid(Duration.newBuilder().setSeconds(seconds).setNanos(nanos)));
|
||||
}
|
||||
}
|
398
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
vendored
Normal file
398
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java
vendored
Normal file
@ -0,0 +1,398 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.protobuf.DynamicMessage;
|
||||
import com.google.protobuf.Message;
|
||||
import com.google.protobuf.UninitializedMessageException;
|
||||
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredMessage;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class FieldMaskTreeTest {
|
||||
@Test
|
||||
public void testAddFieldPath() throws Exception {
|
||||
FieldMaskTree tree = new FieldMaskTree();
|
||||
assertThat(tree.toString()).isEmpty();
|
||||
tree.addFieldPath("");
|
||||
assertThat(tree.toString()).isEmpty();
|
||||
// New branch.
|
||||
tree.addFieldPath("foo");
|
||||
assertThat(tree.toString()).isEqualTo("foo");
|
||||
// Redundant path.
|
||||
tree.addFieldPath("foo");
|
||||
assertThat(tree.toString()).isEqualTo("foo");
|
||||
// New branch.
|
||||
tree.addFieldPath("bar.baz");
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,foo");
|
||||
// Redundant sub-path.
|
||||
tree.addFieldPath("foo.bar");
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,foo");
|
||||
// New branch from a non-root node.
|
||||
tree.addFieldPath("bar.quz");
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz,foo");
|
||||
// A path that matches several existing sub-paths.
|
||||
tree.addFieldPath("bar");
|
||||
assertThat(tree.toString()).isEqualTo("bar,foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeFromFieldMask() throws Exception {
|
||||
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz,foo");
|
||||
tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
|
||||
assertThat(tree.toString()).isEqualTo("bar,foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveFieldPath() throws Exception {
|
||||
String initialTreeString = "bar.baz,bar.quz.bar,foo";
|
||||
FieldMaskTree tree;
|
||||
|
||||
// Empty path.
|
||||
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
|
||||
tree.removeFieldPath("");
|
||||
assertThat(tree.toString()).isEqualTo(initialTreeString);
|
||||
|
||||
// Non-exist sub-path of an existing leaf.
|
||||
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
|
||||
tree.removeFieldPath("foo.bar");
|
||||
assertThat(tree.toString()).isEqualTo(initialTreeString);
|
||||
|
||||
// Non-exist path.
|
||||
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
|
||||
tree.removeFieldPath("bar.foo");
|
||||
assertThat(tree.toString()).isEqualTo(initialTreeString);
|
||||
|
||||
// Match an existing leaf node -> remove leaf node.
|
||||
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
|
||||
tree.removeFieldPath("foo");
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz.bar");
|
||||
|
||||
// Match sub-path of an existing leaf node -> recursive removal.
|
||||
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
|
||||
tree.removeFieldPath("bar.quz.bar");
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,foo");
|
||||
|
||||
// Match a non-leaf node -> remove all children.
|
||||
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
|
||||
tree.removeFieldPath("bar");
|
||||
assertThat(tree.toString()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveFromFieldMask() throws Exception {
|
||||
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
|
||||
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz,foo");
|
||||
tree.removeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
|
||||
assertThat(tree.toString()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
assertThat(result.toString()).isEmpty();
|
||||
// Non-exist path.
|
||||
tree.intersectFieldPath("quz", result);
|
||||
assertThat(result.toString()).isEmpty();
|
||||
// Sub-path of an existing leaf.
|
||||
tree.intersectFieldPath("foo.bar", result);
|
||||
assertThat(result.toString()).isEqualTo("foo.bar");
|
||||
// Match an existing leaf node.
|
||||
tree.intersectFieldPath("foo", result);
|
||||
assertThat(result.toString()).isEqualTo("foo");
|
||||
// Non-exist path.
|
||||
tree.intersectFieldPath("bar.foo", result);
|
||||
assertThat(result.toString()).isEqualTo("foo");
|
||||
// Match a non-leaf node.
|
||||
tree.intersectFieldPath("bar", result);
|
||||
assertThat(result.toString()).isEqualTo("bar.baz,bar.quz,foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMerge() throws Exception {
|
||||
testMergeImpl(true);
|
||||
testMergeImpl(false);
|
||||
testMergeRequire(false);
|
||||
testMergeRequire(true);
|
||||
}
|
||||
|
||||
private void merge(
|
||||
FieldMaskTree tree,
|
||||
Message source,
|
||||
Message.Builder builder,
|
||||
FieldMaskUtil.MergeOptions options,
|
||||
boolean useDynamicMessage)
|
||||
throws Exception {
|
||||
if (useDynamicMessage) {
|
||||
Message.Builder newBuilder =
|
||||
DynamicMessage.newBuilder(source.getDescriptorForType())
|
||||
.mergeFrom(builder.buildPartial().toByteArray());
|
||||
tree.merge(
|
||||
DynamicMessage.newBuilder(source.getDescriptorForType())
|
||||
.mergeFrom(source.toByteArray())
|
||||
.build(),
|
||||
newBuilder,
|
||||
options);
|
||||
builder.clear();
|
||||
builder.mergeFrom(newBuilder.buildPartial());
|
||||
} else {
|
||||
tree.merge(source, builder, options);
|
||||
}
|
||||
}
|
||||
|
||||
private void testMergeRequire(boolean useDynamicMessage) throws Exception {
|
||||
TestRequired value = TestRequired.newBuilder().setA(4321).setB(8765).setC(233333).build();
|
||||
TestRequiredMessage source = TestRequiredMessage.newBuilder().setRequiredMessage(value).build();
|
||||
|
||||
FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
|
||||
TestRequiredMessage.Builder builder = TestRequiredMessage.newBuilder();
|
||||
merge(
|
||||
new FieldMaskTree().addFieldPath("required_message.a"),
|
||||
source,
|
||||
builder,
|
||||
options,
|
||||
useDynamicMessage);
|
||||
assertThat(builder.hasRequiredMessage()).isTrue();
|
||||
assertThat(builder.getRequiredMessage().hasA()).isTrue();
|
||||
assertThat(builder.getRequiredMessage().hasB()).isFalse();
|
||||
assertThat(builder.getRequiredMessage().hasC()).isFalse();
|
||||
merge(
|
||||
new FieldMaskTree().addFieldPath("required_message.b").addFieldPath("required_message.c"),
|
||||
source,
|
||||
builder,
|
||||
options,
|
||||
useDynamicMessage);
|
||||
try {
|
||||
assertThat(source).isEqualTo(builder.build());
|
||||
} catch (UninitializedMessageException e) {
|
||||
throw new AssertionError("required field isn't set", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void testMergeImpl(boolean useDynamicMessage) throws Exception {
|
||||
TestAllTypes value =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1234)
|
||||
.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 with an empty FieldMask.
|
||||
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().addRepeatedInt32(1000);
|
||||
merge(new FieldMaskTree(), source, builder, options, useDynamicMessage);
|
||||
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().addRepeatedInt32(1000);
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
// Test merging each individual field.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().setOptionalInt32(1234);
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.optional_nested_message"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().addRepeatedInt32(4321);
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.repeated_nested_message"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(
|
||||
new FieldMaskTree().addFieldPath("child.payload.optional_int32"),
|
||||
source,
|
||||
builder,
|
||||
options,
|
||||
useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(
|
||||
new FieldMaskTree().addFieldPath("child.payload.optional_nested_message"),
|
||||
source,
|
||||
builder,
|
||||
options,
|
||||
useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected
|
||||
.getChildBuilder()
|
||||
.getPayloadBuilder()
|
||||
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("child.payload.repeated_int32"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected
|
||||
.getChildBuilder()
|
||||
.getPayloadBuilder()
|
||||
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
|
||||
assertThat(builder.build()).isEqualTo(expected.build());
|
||||
|
||||
// Test merging all fields.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("child").addFieldPath("payload"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
assertThat(builder.build()).isEqualTo(source);
|
||||
|
||||
// Test repeated options.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().addRepeatedInt32(1000);
|
||||
merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
// Default behavior is to append repeated fields.
|
||||
assertThat(builder.getPayload().getRepeatedInt32Count()).isEqualTo(2);
|
||||
assertThat(builder.getPayload().getRepeatedInt32(0)).isEqualTo(1000);
|
||||
assertThat(builder.getPayload().getRepeatedInt32(1)).isEqualTo(4321);
|
||||
// Change to replace repeated fields.
|
||||
options.setReplaceRepeatedFields(true);
|
||||
merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
assertThat(builder.getPayload().getRepeatedInt32Count()).isEqualTo(1);
|
||||
assertThat(builder.getPayload().getRepeatedInt32(0)).isEqualTo(4321);
|
||||
|
||||
// Test message options.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().setOptionalInt32(1000);
|
||||
builder.getPayloadBuilder().setOptionalUint32(2000);
|
||||
merge(new FieldMaskTree().addFieldPath("payload"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
// Default behavior is to merge message fields.
|
||||
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(1234);
|
||||
assertThat(builder.getPayload().getOptionalUint32()).isEqualTo(2000);
|
||||
|
||||
// Test merging unset message fields.
|
||||
NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload"),
|
||||
clearedSource, builder, options, useDynamicMessage);
|
||||
assertThat(builder.hasPayload()).isFalse();
|
||||
|
||||
// Skip a message field if they are unset in both source and target.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
|
||||
clearedSource, builder, options, useDynamicMessage);
|
||||
assertThat(builder.hasPayload()).isFalse();
|
||||
|
||||
// Change to replace message fields.
|
||||
options.setReplaceMessageFields(true);
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().setOptionalInt32(1000);
|
||||
builder.getPayloadBuilder().setOptionalUint32(2000);
|
||||
merge(new FieldMaskTree().addFieldPath("payload"),
|
||||
source, builder, options, useDynamicMessage);
|
||||
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(1234);
|
||||
assertThat(builder.getPayload().getOptionalUint32()).isEqualTo(0);
|
||||
|
||||
// Test merging unset message fields.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().setOptionalInt32(1000);
|
||||
builder.getPayloadBuilder().setOptionalUint32(2000);
|
||||
merge(new FieldMaskTree().addFieldPath("payload"),
|
||||
clearedSource, builder, options, useDynamicMessage);
|
||||
assertThat(builder.hasPayload()).isFalse();
|
||||
|
||||
// Test merging unset primitive fields.
|
||||
builder = source.toBuilder();
|
||||
builder.getPayloadBuilder().clearOptionalInt32();
|
||||
NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build();
|
||||
builder = source.toBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
|
||||
sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
|
||||
assertThat(builder.getPayload().hasOptionalInt32()).isTrue();
|
||||
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(0);
|
||||
|
||||
// Change to clear unset primitive fields.
|
||||
options.setReplacePrimitiveFields(true);
|
||||
builder = source.toBuilder();
|
||||
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
|
||||
sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
|
||||
assertThat(builder.hasPayload()).isTrue();
|
||||
assertThat(builder.getPayload().hasOptionalInt32()).isFalse();
|
||||
}
|
||||
}
|
315
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
vendored
Normal file
315
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.protobuf.FieldMask;
|
||||
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link FieldMaskUtil}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class FieldMaskUtilTest {
|
||||
@Test
|
||||
public void testIsValid() throws Exception {
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload")).isTrue();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist")).isFalse();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32")).isTrue();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_int32")).isTrue();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message"))
|
||||
.isTrue();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message"))
|
||||
.isTrue();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.nonexist")).isFalse();
|
||||
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
FieldMaskUtil.isValid(
|
||||
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")))
|
||||
.isFalse();
|
||||
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload")).isTrue();
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist")).isFalse();
|
||||
|
||||
assertThat(
|
||||
FieldMaskUtil.isValid(
|
||||
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
FieldMaskUtil.isValid(
|
||||
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")))
|
||||
.isFalse();
|
||||
|
||||
assertThat(
|
||||
FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message.bb"))
|
||||
.isTrue();
|
||||
// Repeated fields cannot have sub-paths.
|
||||
assertThat(
|
||||
FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message.bb"))
|
||||
.isFalse();
|
||||
// Non-message fields cannot have sub-paths.
|
||||
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32.bb"))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() throws Exception {
|
||||
assertThat(FieldMaskUtil.toString(FieldMask.getDefaultInstance())).isEmpty();
|
||||
FieldMask mask = FieldMask.newBuilder().addPaths("foo").build();
|
||||
assertThat(FieldMaskUtil.toString(mask)).isEqualTo("foo");
|
||||
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar").build();
|
||||
assertThat(FieldMaskUtil.toString(mask)).isEqualTo("foo,bar");
|
||||
|
||||
// Empty field paths are ignored.
|
||||
mask =
|
||||
FieldMask.newBuilder()
|
||||
.addPaths("")
|
||||
.addPaths("foo")
|
||||
.addPaths("")
|
||||
.addPaths("bar")
|
||||
.addPaths("")
|
||||
.build();
|
||||
assertThat(FieldMaskUtil.toString(mask)).isEqualTo("foo,bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromString() throws Exception {
|
||||
FieldMask mask = FieldMaskUtil.fromString("");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(0);
|
||||
mask = FieldMaskUtil.fromString("foo");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(1);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("foo");
|
||||
mask = FieldMaskUtil.fromString("foo,bar.baz");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(2);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("foo");
|
||||
assertThat(mask.getPaths(1)).isEqualTo("bar.baz");
|
||||
|
||||
// Empty field paths are ignore.
|
||||
mask = FieldMaskUtil.fromString(",foo,,bar,");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(2);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("foo");
|
||||
assertThat(mask.getPaths(1)).isEqualTo("bar");
|
||||
|
||||
// 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");
|
||||
assertWithMessage("Exception is expected.").fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromFieldNumbers() throws Exception {
|
||||
FieldMask mask = FieldMaskUtil.fromFieldNumbers(TestAllTypes.class);
|
||||
assertThat(mask.getPathsCount()).isEqualTo(0);
|
||||
mask =
|
||||
FieldMaskUtil.fromFieldNumbers(
|
||||
TestAllTypes.class, TestAllTypes.OPTIONAL_INT32_FIELD_NUMBER);
|
||||
assertThat(mask.getPathsCount()).isEqualTo(1);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("optional_int32");
|
||||
mask =
|
||||
FieldMaskUtil.fromFieldNumbers(
|
||||
TestAllTypes.class,
|
||||
TestAllTypes.OPTIONAL_INT32_FIELD_NUMBER,
|
||||
TestAllTypes.OPTIONAL_INT64_FIELD_NUMBER);
|
||||
assertThat(mask.getPathsCount()).isEqualTo(2);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("optional_int32");
|
||||
assertThat(mask.getPaths(1)).isEqualTo("optional_int64");
|
||||
|
||||
try {
|
||||
int invalidFieldNumber = 1000;
|
||||
mask = FieldMaskUtil.fromFieldNumbers(TestAllTypes.class, invalidFieldNumber);
|
||||
assertWithMessage("Exception is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToJsonString() throws Exception {
|
||||
FieldMask mask = FieldMask.getDefaultInstance();
|
||||
assertThat(FieldMaskUtil.toJsonString(mask)).isEmpty();
|
||||
mask = FieldMask.newBuilder().addPaths("foo").build();
|
||||
assertThat(FieldMaskUtil.toJsonString(mask)).isEqualTo("foo");
|
||||
mask = FieldMask.newBuilder().addPaths("foo.bar_baz").addPaths("").build();
|
||||
assertThat(FieldMaskUtil.toJsonString(mask)).isEqualTo("foo.barBaz");
|
||||
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar_baz").build();
|
||||
assertThat(FieldMaskUtil.toJsonString(mask)).isEqualTo("foo,barBaz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromJsonString() throws Exception {
|
||||
FieldMask mask = FieldMaskUtil.fromJsonString("");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(0);
|
||||
mask = FieldMaskUtil.fromJsonString("foo");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(1);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("foo");
|
||||
mask = FieldMaskUtil.fromJsonString("foo.barBaz");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(1);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("foo.bar_baz");
|
||||
mask = FieldMaskUtil.fromJsonString("foo,barBaz");
|
||||
assertThat(mask.getPathsCount()).isEqualTo(2);
|
||||
assertThat(mask.getPaths(0)).isEqualTo("foo");
|
||||
assertThat(mask.getPaths(1)).isEqualTo("bar_baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromStringList() throws Exception {
|
||||
FieldMask mask =
|
||||
FieldMaskUtil.fromStringList(
|
||||
NestedTestAllTypes.class, ImmutableList.of("payload.repeated_nested_message", "child"));
|
||||
assertThat(mask)
|
||||
.isEqualTo(
|
||||
FieldMask.newBuilder()
|
||||
.addPaths("payload.repeated_nested_message")
|
||||
.addPaths("child")
|
||||
.build());
|
||||
|
||||
mask =
|
||||
FieldMaskUtil.fromStringList(
|
||||
NestedTestAllTypes.getDescriptor(),
|
||||
ImmutableList.of("payload.repeated_nested_message", "child"));
|
||||
assertThat(mask)
|
||||
.isEqualTo(
|
||||
FieldMask.newBuilder()
|
||||
.addPaths("payload.repeated_nested_message")
|
||||
.addPaths("child")
|
||||
.build());
|
||||
|
||||
mask =
|
||||
FieldMaskUtil.fromStringList(ImmutableList.of("payload.repeated_nested_message", "child"));
|
||||
assertThat(mask)
|
||||
.isEqualTo(
|
||||
FieldMask.newBuilder()
|
||||
.addPaths("payload.repeated_nested_message")
|
||||
.addPaths("child")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
assertThat(FieldMaskUtil.toString(result)).isEqualTo("bar,foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnion_usingVarArgs() throws Exception {
|
||||
FieldMask mask1 = FieldMaskUtil.fromString("foo");
|
||||
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar.quz");
|
||||
FieldMask mask3 = FieldMaskUtil.fromString("bar.quz");
|
||||
FieldMask mask4 = FieldMaskUtil.fromString("bar");
|
||||
FieldMask result = FieldMaskUtil.union(mask1, mask2, mask3, mask4);
|
||||
assertThat(FieldMaskUtil.toString(result)).isEqualTo("bar,foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubstract() throws Exception {
|
||||
// Only test a simple case here and expect
|
||||
// {@link FieldMaskTreeTest#testRemoveFieldPath} to cover all scenarios.
|
||||
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz");
|
||||
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar");
|
||||
FieldMask result = FieldMaskUtil.subtract(mask1, mask2);
|
||||
assertThat(FieldMaskUtil.toString(result)).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubstract_usingVarArgs() throws Exception {
|
||||
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz.bar");
|
||||
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar.baz.quz");
|
||||
FieldMask mask3 = FieldMaskUtil.fromString("bar.quz");
|
||||
FieldMask mask4 = FieldMaskUtil.fromString("foo,bar.baz");
|
||||
FieldMask result = FieldMaskUtil.subtract(mask1, mask2, mask3, mask4);
|
||||
assertThat(FieldMaskUtil.toString(result)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
assertThat(FieldMaskUtil.toString(result)).isEqualTo("bar.baz,bar.quz,foo.bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrim() throws Exception {
|
||||
NestedTestAllTypes source =
|
||||
NestedTestAllTypes.newBuilder()
|
||||
.setPayload(
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1234)
|
||||
.setOptionalString("1234")
|
||||
.setOptionalBool(true))
|
||||
.build();
|
||||
FieldMask mask =
|
||||
FieldMaskUtil.fromStringList(
|
||||
ImmutableList.of("payload.optional_int32", "payload.optional_string"));
|
||||
|
||||
NestedTestAllTypes actual = FieldMaskUtil.trim(mask, source);
|
||||
|
||||
assertThat(actual)
|
||||
.isEqualTo(
|
||||
NestedTestAllTypes.newBuilder()
|
||||
.setPayload(
|
||||
TestAllTypes.newBuilder().setOptionalInt32(1234).setOptionalString("1234"))
|
||||
.build());
|
||||
}
|
||||
}
|
1984
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
vendored
Normal file
1984
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
vendored
Normal file
File diff suppressed because it is too large
Load Diff
65
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/StructsTest.java
vendored
Normal file
65
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/StructsTest.java
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.protobuf.Struct;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public final class StructsTest {
|
||||
|
||||
@Test
|
||||
public void test1pair_constructsObject() throws Exception {
|
||||
Struct.Builder expected = Struct.newBuilder();
|
||||
JsonFormat.parser().merge("{\"k1\": 1}", expected);
|
||||
assertThat(Structs.of("k1", Values.of(1))).isEqualTo(expected.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2pair_constructsObject() throws Exception {
|
||||
Struct.Builder expected = Struct.newBuilder();
|
||||
JsonFormat.parser().merge("{\"k1\": 1, \"k2\": 2}", expected);
|
||||
assertThat(Structs.of("k1", Values.of(1), "k2", Values.of(2))).isEqualTo(expected.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3pair_constructsObject() throws Exception {
|
||||
Struct.Builder expected = Struct.newBuilder();
|
||||
JsonFormat.parser().merge("{\"k1\": 1, \"k2\": 2, \"k3\": 3}", expected);
|
||||
assertThat(Structs.of("k1", Values.of(1), "k2", Values.of(2), "k3", Values.of(3)))
|
||||
.isEqualTo(expected.build());
|
||||
}
|
||||
|
||||
}
|
829
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java
vendored
Normal file
829
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java
vendored
Normal file
@ -0,0 +1,829 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static com.google.protobuf.util.DurationsTest.duration;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.protobuf.Duration;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link Timestamps}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class TimestampsTest {
|
||||
private static final int MILLIS_PER_SECOND = 1000;
|
||||
private static final long MILLIS = 1409130915111L;
|
||||
private static final long SECONDS = MILLIS / MILLIS_PER_SECOND;
|
||||
private static final long MICROS = MILLIS * 1000;
|
||||
private static final long NANOS = MICROS * 1000;
|
||||
|
||||
@SuppressWarnings("ConstantOverflow")
|
||||
private static final long MAX_VALUE = Long.MAX_VALUE * MILLIS_PER_SECOND + MILLIS_PER_SECOND;
|
||||
|
||||
@SuppressWarnings("ConstantOverflow")
|
||||
private static final long MIN_VALUE = Long.MIN_VALUE * MILLIS_PER_SECOND;
|
||||
|
||||
private static final Timestamp TIMESTAMP = timestamp(1409130915, 111000000);
|
||||
private static final Timestamp ZERO_TIMESTAMP = timestamp(0, 0);
|
||||
private static final Timestamp ONE_OF_TIMESTAMP = timestamp(-1, 999000000);
|
||||
|
||||
private static final Timestamp INVALID_MAX =
|
||||
Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).setNanos(Integer.MAX_VALUE).build();
|
||||
private static final Timestamp INVALID_MIN =
|
||||
Timestamp.newBuilder().setSeconds(Long.MIN_VALUE).setNanos(Integer.MIN_VALUE).build();
|
||||
|
||||
@Test
|
||||
public void testMinMaxAreValid() {
|
||||
assertThat(Timestamps.isValid(Timestamps.MAX_VALUE)).isTrue();
|
||||
assertThat(Timestamps.isValid(Timestamps.MIN_VALUE)).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsValid_false() {
|
||||
assertThat(Timestamps.isValid(0L, -1)).isFalse();
|
||||
assertThat(Timestamps.isValid(1L, -1)).isFalse();
|
||||
assertThat(Timestamps.isValid(1L, (int) Timestamps.NANOS_PER_SECOND)).isFalse();
|
||||
assertThat(Timestamps.isValid(-62135596801L, 0)).isFalse();
|
||||
assertThat(Timestamps.isValid(253402300800L, 0)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValid_true() {
|
||||
assertThat(Timestamps.isValid(0L, 0)).isTrue();
|
||||
assertThat(Timestamps.isValid(1L, 0)).isTrue();
|
||||
assertThat(Timestamps.isValid(1L, 1)).isTrue();
|
||||
assertThat(Timestamps.isValid(42L, 0)).isTrue();
|
||||
assertThat(Timestamps.isValid(42L, 42)).isTrue();
|
||||
assertThat(Timestamps.isValid(-62135596800L, 0)).isTrue();
|
||||
assertThat(Timestamps.isValid(-62135596800L, 1)).isTrue();
|
||||
assertThat(Timestamps.isValid(62135596799L, 1)).isTrue();
|
||||
assertThat(Timestamps.isValid(253402300799L, 0)).isTrue();
|
||||
assertThat(Timestamps.isValid(253402300798L, 1)).isTrue();
|
||||
assertThat(Timestamps.isValid(253402300798L, (int) (Timestamps.NANOS_PER_SECOND - 1))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampStringFormat() throws Exception {
|
||||
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
|
||||
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
|
||||
assertThat(start.getSeconds()).isEqualTo(Timestamps.TIMESTAMP_SECONDS_MIN);
|
||||
assertThat(start.getNanos()).isEqualTo(0);
|
||||
assertThat(end.getSeconds()).isEqualTo(Timestamps.TIMESTAMP_SECONDS_MAX);
|
||||
assertThat(end.getNanos()).isEqualTo(999999999);
|
||||
assertThat(Timestamps.toString(start)).isEqualTo("0001-01-01T00:00:00Z");
|
||||
assertThat(Timestamps.toString(end)).isEqualTo("9999-12-31T23:59:59.999999999Z");
|
||||
|
||||
Timestamp value = Timestamps.parse("1970-01-01T00:00:00Z");
|
||||
assertThat(value.getSeconds()).isEqualTo(0);
|
||||
assertThat(value.getNanos()).isEqualTo(0);
|
||||
value = Timestamps.parseUnchecked("1970-01-01T00:00:00Z");
|
||||
assertThat(value.getSeconds()).isEqualTo(0);
|
||||
assertThat(value.getNanos()).isEqualTo(0);
|
||||
|
||||
// Test negative timestamps.
|
||||
value = Timestamps.parse("1969-12-31T23:59:59.999Z");
|
||||
assertThat(value.getSeconds()).isEqualTo(-1);
|
||||
// Nano part is in the range of [0, 999999999] for Timestamp.
|
||||
assertThat(value.getNanos()).isEqualTo(999000000);
|
||||
value = Timestamps.parseUnchecked("1969-12-31T23:59:59.999Z");
|
||||
assertThat(value.getSeconds()).isEqualTo(-1);
|
||||
// Nano part is in the range of [0, 999999999] for Timestamp.
|
||||
assertThat(value.getNanos()).isEqualTo(999000000);
|
||||
|
||||
// Test that 3, 6, or 9 digits are used for the fractional part.
|
||||
value = Timestamp.newBuilder().setNanos(10).build();
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.000000010Z");
|
||||
value = Timestamp.newBuilder().setNanos(10000).build();
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.000010Z");
|
||||
value = Timestamp.newBuilder().setNanos(10000000).build();
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.010Z");
|
||||
|
||||
// Test that parsing accepts timezone offsets.
|
||||
value = Timestamps.parse("1970-01-01T00:00:00.010+08:00");
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1969-12-31T16:00:00.010Z");
|
||||
value = Timestamps.parse("1970-01-01T00:00:00.010-08:00");
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T08:00:00.010Z");
|
||||
value = Timestamps.parseUnchecked("1970-01-01T00:00:00.010+08:00");
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1969-12-31T16:00:00.010Z");
|
||||
value = Timestamps.parseUnchecked("1970-01-01T00:00:00.010-08:00");
|
||||
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T08:00:00.010Z");
|
||||
}
|
||||
|
||||
private volatile boolean stopParsingThreads = false;
|
||||
private volatile String errorMessage = "";
|
||||
|
||||
private class ParseTimestampThread extends Thread {
|
||||
private final String[] strings;
|
||||
private final Timestamp[] values;
|
||||
|
||||
public ParseTimestampThread(String[] strings, Timestamp[] values) {
|
||||
this.strings = strings;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int index = 0;
|
||||
while (!stopParsingThreads) {
|
||||
Timestamp result;
|
||||
try {
|
||||
result = Timestamps.parse(strings[index]);
|
||||
} catch (ParseException e) {
|
||||
errorMessage = "Failed to parse timestamp: " + strings[index];
|
||||
break;
|
||||
}
|
||||
if (result.getSeconds() != values[index].getSeconds()
|
||||
|| result.getNanos() != values[index].getNanos()) {
|
||||
errorMessage =
|
||||
"Actual result: " + result.toString() + ", expected: " + values[index].toString();
|
||||
break;
|
||||
}
|
||||
index = (index + 1) % strings.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampConcurrentParsing() throws Exception {
|
||||
String[] timestampStrings =
|
||||
new String[] {
|
||||
"0001-01-01T00:00:00Z",
|
||||
"9999-12-31T23:59:59.999999999Z",
|
||||
"1970-01-01T00:00:00Z",
|
||||
"1969-12-31T23:59:59.999Z",
|
||||
};
|
||||
Timestamp[] timestampValues = new Timestamp[timestampStrings.length];
|
||||
for (int i = 0; i < timestampStrings.length; i++) {
|
||||
timestampValues[i] = Timestamps.parse(timestampStrings[i]);
|
||||
}
|
||||
|
||||
final int threadCount = 16;
|
||||
final int runningTime = 5000; // in milliseconds.
|
||||
final List<Thread> threads = new ArrayList<>();
|
||||
|
||||
stopParsingThreads = false;
|
||||
errorMessage = "";
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
Thread thread = new ParseTimestampThread(timestampStrings, timestampValues);
|
||||
thread.start();
|
||||
threads.add(thread);
|
||||
}
|
||||
Thread.sleep(runningTime);
|
||||
stopParsingThreads = true;
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
assertThat(errorMessage).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatValueTooSmall() throws Exception {
|
||||
try {
|
||||
// Value too small.
|
||||
Timestamp value =
|
||||
Timestamp.newBuilder().setSeconds(Timestamps.TIMESTAMP_SECONDS_MIN - 1).build();
|
||||
Timestamps.toString(value);
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatValueTooLarge() throws Exception {
|
||||
try {
|
||||
// Value too large.
|
||||
Timestamp value =
|
||||
Timestamp.newBuilder().setSeconds(Timestamps.TIMESTAMP_SECONDS_MAX + 1).build();
|
||||
Timestamps.toString(value);
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatNanosTooSmall() throws Exception {
|
||||
try {
|
||||
// Invalid nanos value.
|
||||
Timestamp value = Timestamp.newBuilder().setNanos(-1).build();
|
||||
Timestamps.toString(value);
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatNanosTooLarge() throws Exception {
|
||||
try {
|
||||
// Invalid nanos value.
|
||||
Timestamp value = Timestamp.newBuilder().setNanos(1000000000).build();
|
||||
Timestamps.toString(value);
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatDateTooSmall() {
|
||||
try {
|
||||
Timestamps.parse("0000-01-01T00:00:00Z");
|
||||
Assert.fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
assertThat(expected).hasCauseThat().isNotNull();
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("0000-01-01T00:00:00Z");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatDateTooLarge() {
|
||||
try {
|
||||
Timestamps.parse("10000-01-01T00:00:00Z");
|
||||
Assert.fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("10000-01-01T00:00:00Z");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatMissingT() {
|
||||
try {
|
||||
Timestamps.parse("1970-01-01 00:00:00Z");
|
||||
Assert.fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("1970-01-01 00:00:00Z");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidFormatMissingZ() {
|
||||
try {
|
||||
Timestamps.parse("1970-01-01T00:00:00");
|
||||
assertWithMessage("ParseException is expected.").fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("1970-01-01T00:00:00");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidOffset() {
|
||||
try {
|
||||
Timestamps.parse("1970-01-01T00:00:00+0000");
|
||||
assertWithMessage("ParseException is expected.").fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("1970-01-01T00:00:00+0000");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidTrailingText() {
|
||||
try {
|
||||
Timestamps.parse("1970-01-01T00:00:00Z0");
|
||||
assertWithMessage("ParseException is expected.").fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("1970-01-01T00:00:00Z0");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampInvalidNanoSecond() {
|
||||
try {
|
||||
Timestamps.parse("1970-01-01T00:00:00.ABCZ");
|
||||
assertWithMessage("ParseException is expected.").fail();
|
||||
} catch (ParseException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
try {
|
||||
Timestamps.parseUnchecked("1970-01-01T00:00:00.ABCZ");
|
||||
assertWithMessage("IllegalArgumentException is expected.").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
Assert.assertNotNull(expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampConversion() throws Exception {
|
||||
Timestamp timestamp = Timestamps.parse("1970-01-01T00:00:01.111111111Z");
|
||||
assertThat(Timestamps.toNanos(timestamp)).isEqualTo(1111111111);
|
||||
assertThat(Timestamps.toMicros(timestamp)).isEqualTo(1111111);
|
||||
assertThat(Timestamps.toMillis(timestamp)).isEqualTo(1111);
|
||||
assertThat(Timestamps.toSeconds(timestamp)).isEqualTo(1);
|
||||
timestamp = Timestamps.fromNanos(1111111111);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111111111Z");
|
||||
timestamp = Timestamps.fromMicros(1111111);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111111Z");
|
||||
timestamp = Timestamps.fromMillis(1111);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z");
|
||||
timestamp = Timestamps.fromSeconds(1);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01Z");
|
||||
|
||||
timestamp = Timestamps.parse("1969-12-31T23:59:59.111111111Z");
|
||||
assertThat(Timestamps.toNanos(timestamp)).isEqualTo(-888888889);
|
||||
assertThat(Timestamps.toMicros(timestamp)).isEqualTo(-888889);
|
||||
assertThat(Timestamps.toMillis(timestamp)).isEqualTo(-889);
|
||||
assertThat(Timestamps.toSeconds(timestamp)).isEqualTo(-1);
|
||||
timestamp = Timestamps.fromNanos(-888888889);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111111111Z");
|
||||
timestamp = Timestamps.fromMicros(-888889);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111111Z");
|
||||
timestamp = Timestamps.fromMillis(-889);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111Z");
|
||||
timestamp = Timestamps.fromSeconds(-1);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromDate() {
|
||||
Date date = new Date(1111);
|
||||
Timestamp timestamp = Timestamps.fromDate(date);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromDate_after9999CE() {
|
||||
// protobuf still requires Java 7 so no java.time :-(
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.clear(); // avoid random number of milliseconds
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("GMT-0"));
|
||||
calendar.set(20000, Calendar.OCTOBER, 20, 5, 4, 3);
|
||||
Date date = calendar.getTime();
|
||||
try {
|
||||
Timestamps.fromDate(date);
|
||||
Assert.fail("should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromDate_beforeYear1() {
|
||||
// protobuf still requires Java 7 so no java.time :-(
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.clear(); // avoid random number of milliseconds
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("GMT-0"));
|
||||
calendar.set(-32, Calendar.OCTOBER, 20, 5, 4, 3);
|
||||
Date date = calendar.getTime();
|
||||
try {
|
||||
Timestamps.fromDate(date);
|
||||
Assert.fail("should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromDate_after2262CE() {
|
||||
// protobuf still requires Java 7 so no java.time :-(
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.clear(); // avoid random number of milliseconds
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("GMT-0"));
|
||||
calendar.set(2299, Calendar.OCTOBER, 20, 5, 4, 3);
|
||||
Date date = calendar.getTime();
|
||||
Timestamp timestamp = Timestamps.fromDate(date);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("2299-10-20T05:04:03Z");
|
||||
}
|
||||
|
||||
/* Timestamp only stores integral seconds in the Date parent class and stores the nanosecond
|
||||
* adjustment in the Timestamp class. */
|
||||
@Test
|
||||
public void testFromSqlTimestampSubMillisecondPrecision() {
|
||||
java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(1111);
|
||||
sqlTimestamp.setNanos(sqlTimestamp.getNanos() + 234567);
|
||||
Timestamp timestamp = Timestamps.fromDate(sqlTimestamp);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111234567Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSqlTimestamp() {
|
||||
Date date = new java.sql.Timestamp(1111);
|
||||
Timestamp timestamp = Timestamps.fromDate(date);
|
||||
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeOperations() throws Exception {
|
||||
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
|
||||
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
|
||||
|
||||
Duration duration = Timestamps.between(start, end);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s");
|
||||
Timestamp value = Timestamps.add(start, duration);
|
||||
assertThat(value).isEqualTo(end);
|
||||
value = Timestamps.subtract(end, duration);
|
||||
assertThat(value).isEqualTo(start);
|
||||
|
||||
duration = Timestamps.between(end, start);
|
||||
assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s");
|
||||
value = Timestamps.add(end, duration);
|
||||
assertThat(value).isEqualTo(start);
|
||||
value = Timestamps.subtract(start, duration);
|
||||
assertThat(value).isEqualTo(end);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComparator() {
|
||||
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 2))).isEqualTo(0);
|
||||
assertThat(Timestamps.compare(timestamp(0, 0), timestamp(0, 0))).isEqualTo(0);
|
||||
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(1, 1))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 1))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(1, 1), timestamp(3, 1))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(3, 2))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-1, 1))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-3, 2), timestamp(-3, 3))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-1, 1), timestamp(-3, 1))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-3, 2))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-10, 1), timestamp(1, 1))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(0, 1), timestamp(0, 1))).isEqualTo(0);
|
||||
assertThat(Timestamps.compare(timestamp(0x80000000L, 0), timestamp(0, 0))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(0xFFFFFFFF00000000L, 0), timestamp(0, 0)))
|
||||
.isLessThan(0);
|
||||
|
||||
Timestamp timestamp0 = timestamp(-50, 400);
|
||||
Timestamp timestamp1 = timestamp(-50, 500);
|
||||
Timestamp timestamp2 = timestamp(50, 500);
|
||||
Timestamp timestamp3 = timestamp(100, 20);
|
||||
Timestamp timestamp4 = timestamp(100, 50);
|
||||
Timestamp timestamp5 = timestamp(100, 150);
|
||||
Timestamp timestamp6 = timestamp(150, 40);
|
||||
|
||||
List<Timestamp> timestamps =
|
||||
Lists.newArrayList(
|
||||
timestamp5,
|
||||
timestamp3,
|
||||
timestamp1,
|
||||
timestamp4,
|
||||
timestamp6,
|
||||
timestamp2,
|
||||
timestamp0,
|
||||
timestamp3);
|
||||
|
||||
Collections.sort(timestamps, Timestamps.comparator());
|
||||
assertThat(timestamps)
|
||||
.containsExactly(
|
||||
timestamp0,
|
||||
timestamp1,
|
||||
timestamp2,
|
||||
timestamp3,
|
||||
timestamp3,
|
||||
timestamp4,
|
||||
timestamp5,
|
||||
timestamp6)
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompare() {
|
||||
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 2))).isEqualTo(0);
|
||||
assertThat(Timestamps.compare(timestamp(0, 0), timestamp(0, 0))).isEqualTo(0);
|
||||
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(1, 1))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 1))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(1, 1), timestamp(3, 1))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(3, 2))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-1, 1))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-3, 2), timestamp(-3, 3))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-1, 1), timestamp(-3, 1))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-3, 2))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(-10, 1), timestamp(1, 1))).isLessThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(0, 1), timestamp(0, 1))).isEqualTo(0);
|
||||
assertThat(Timestamps.compare(timestamp(0x80000000L, 0), timestamp(0, 0))).isGreaterThan(0);
|
||||
assertThat(Timestamps.compare(timestamp(0xFFFFFFFF00000000L, 0), timestamp(0, 0)))
|
||||
.isLessThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverflowsArithmeticException() throws Exception {
|
||||
try {
|
||||
Timestamps.toNanos(Timestamps.parse("9999-12-31T23:59:59.999999999Z"));
|
||||
assertWithMessage("Expected an ArithmeticException to be thrown").fail();
|
||||
} catch (ArithmeticException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPositiveOverflow() {
|
||||
try {
|
||||
Timestamps.add(Timestamps.MAX_VALUE, Durations.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeOverflow() {
|
||||
try {
|
||||
Timestamps.subtract(Timestamps.MIN_VALUE, Durations.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMaxNanosecondsOverflow() {
|
||||
try {
|
||||
Timestamps.toNanos(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void testInvalidMaxMicrosecondsOverflow() {
|
||||
try {
|
||||
Timestamps.toMicros(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMaxMillisecondsOverflow() {
|
||||
try {
|
||||
Timestamps.toMillis(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMaxSecondsOverflow() {
|
||||
try {
|
||||
Timestamps.toSeconds(INVALID_MAX);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMinNanosecondsOverflow() {
|
||||
try {
|
||||
Timestamps.toNanos(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMicrosecondsMinOverflow() {
|
||||
try {
|
||||
Timestamps.toMicros(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMinMillisecondsOverflow() {
|
||||
try {
|
||||
Timestamps.toMillis(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverInvalidMinSecondsflow() {
|
||||
try {
|
||||
Timestamps.toSeconds(INVALID_MIN);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxNanosecondsConversion() {
|
||||
assertThat(Timestamps.toString(Timestamps.fromNanos(Long.MAX_VALUE)))
|
||||
.isEqualTo("2262-04-11T23:47:16.854775807Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalArgumentExceptionForMaxMicroseconds() {
|
||||
try {
|
||||
Timestamps.fromMicros(Long.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIllegalArgumentExceptionForMaxMilliseconds() {
|
||||
try {
|
||||
Durations.fromMillis(Long.MAX_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinNanosecondsConversion() {
|
||||
assertThat(Timestamps.toString(Timestamps.fromNanos(Long.MIN_VALUE)))
|
||||
.isEqualTo("1677-09-21T00:12:43.145224192Z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalArgumentExceptionForMinMicroseconds() {
|
||||
try {
|
||||
Timestamps.fromMicros(Long.MIN_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIllegalArgumentExceptionForMinMilliseconds() {
|
||||
try {
|
||||
Timestamps.fromMillis(Long.MIN_VALUE);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromSeconds() {
|
||||
assertThat(Timestamps.fromSeconds(SECONDS).getSeconds()).isEqualTo(TIMESTAMP.getSeconds());
|
||||
assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromSeconds(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromSeconds(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromMillis() {
|
||||
assertThat(Timestamps.fromMillis(MILLIS)).isEqualTo(TIMESTAMP);
|
||||
assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromMillis(-1)).isEqualTo(ONE_OF_TIMESTAMP);
|
||||
assertThat(Timestamps.fromMillis(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromMillis(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromMicros() {
|
||||
assertThat(Timestamps.fromMicros(MICROS)).isEqualTo(TIMESTAMP);
|
||||
assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromMicros(-1000)).isEqualTo(ONE_OF_TIMESTAMP);
|
||||
assertThat(Timestamps.fromMicros(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromMicros(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToSeconds() {
|
||||
assertThat(Timestamps.toSeconds(TIMESTAMP)).isEqualTo(SECONDS);
|
||||
assertThat(Timestamps.toSeconds(ZERO_TIMESTAMP)).isEqualTo(0);
|
||||
assertThat(Timestamps.toSeconds(ONE_OF_TIMESTAMP)).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMillis() {
|
||||
assertThat(Timestamps.toMillis(TIMESTAMP)).isEqualTo(MILLIS);
|
||||
assertThat(Timestamps.toMillis(ZERO_TIMESTAMP)).isEqualTo(0);
|
||||
assertThat(Timestamps.toMillis(ONE_OF_TIMESTAMP)).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToMicros() {
|
||||
assertThat(Timestamps.toMicros(TIMESTAMP)).isEqualTo(MICROS);
|
||||
assertThat(Timestamps.toMicros(ZERO_TIMESTAMP)).isEqualTo(0);
|
||||
assertThat(Timestamps.toMicros(ONE_OF_TIMESTAMP)).isEqualTo(-1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromMillisAboveTimestampMaxLimit() {
|
||||
long timestampMaxSeconds = 253402300799L;
|
||||
try {
|
||||
Timestamps.fromMillis((timestampMaxSeconds + 1) * MILLIS_PER_SECOND);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromMillisBelowTimestampMaxLimit() {
|
||||
long timestampMinSeconds = -62135596800L;
|
||||
try {
|
||||
Timestamps.fromMillis((timestampMinSeconds - 1) * MILLIS_PER_SECOND);
|
||||
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFromNanos() {
|
||||
assertThat(Timestamps.fromNanos(NANOS)).isEqualTo(TIMESTAMP);
|
||||
assertThat(Timestamps.fromNanos(0)).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromNanos(-1000000)).isEqualTo(ONE_OF_TIMESTAMP);
|
||||
assertThat(Timestamps.fromNanos(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
assertThat(Timestamps.fromNanos(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToNanos() {
|
||||
assertThat(Timestamps.toNanos(TIMESTAMP)).isEqualTo(NANOS);
|
||||
assertThat(Timestamps.toNanos(ZERO_TIMESTAMP)).isEqualTo(0);
|
||||
assertThat(Timestamps.toNanos(ONE_OF_TIMESTAMP)).isEqualTo(-1000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
assertThat(Timestamps.add(timestamp(1, 10), duration(1, 20))).isEqualTo(timestamp(2, 30));
|
||||
assertThat(Timestamps.add(timestamp(1, 10), duration(-1, -11)))
|
||||
.isEqualTo(timestamp(-1, 999999999));
|
||||
assertThat(Timestamps.add(timestamp(10, 10), duration(-1, -11)))
|
||||
.isEqualTo(timestamp(8, 999999999));
|
||||
assertThat(Timestamps.add(timestamp(1, 1), duration(1, 999999999))).isEqualTo(timestamp(3, 0));
|
||||
assertThat(Timestamps.add(timestamp(1, 2), duration(1, 999999999))).isEqualTo(timestamp(3, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtractDuration() {
|
||||
assertThat(Timestamps.subtract(timestamp(1, 10), duration(-1, -20)))
|
||||
.isEqualTo(timestamp(2, 30));
|
||||
assertThat(Timestamps.subtract(timestamp(1, 10), duration(1, 11)))
|
||||
.isEqualTo(timestamp(-1, 999999999));
|
||||
assertThat(Timestamps.subtract(timestamp(10, 10), duration(1, 11)))
|
||||
.isEqualTo(timestamp(8, 999999999));
|
||||
assertThat(Timestamps.subtract(timestamp(1, 1), duration(-1, -999999999)))
|
||||
.isEqualTo(timestamp(3, 0));
|
||||
assertThat(Timestamps.subtract(timestamp(1, 2), duration(-1, -999999999)))
|
||||
.isEqualTo(timestamp(3, 1));
|
||||
}
|
||||
|
||||
static Timestamp timestamp(long seconds, int nanos) {
|
||||
return Timestamps.checkValid(
|
||||
Timestamps.checkValid(Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos)));
|
||||
}
|
||||
}
|
112
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/ValuesTest.java
vendored
Normal file
112
deps/protobuf/java/util/src/test/java/com/google/protobuf/util/ValuesTest.java
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.protobuf.ListValue;
|
||||
import com.google.protobuf.NullValue;
|
||||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.Value;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public final class ValuesTest {
|
||||
@Test
|
||||
public void testOfNull_IsNullValue() throws Exception {
|
||||
assertThat(Values.ofNull())
|
||||
.isEqualTo(Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfBoolean_ConstructsValue() {
|
||||
assertThat(Values.of(true)).isEqualTo(Value.newBuilder().setBoolValue(true).build());
|
||||
assertThat(Values.of(false)).isEqualTo(Value.newBuilder().setBoolValue(false).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfNumeric_ConstructsValue() {
|
||||
assertThat(Values.of(100)).isEqualTo(Value.newBuilder().setNumberValue(100).build());
|
||||
assertThat(Values.of(1000L)).isEqualTo(Value.newBuilder().setNumberValue(1000).build());
|
||||
assertThat(Values.of(1000.23f)).isEqualTo(Value.newBuilder().setNumberValue(1000.23f).build());
|
||||
assertThat(Values.of(10000.23)).isEqualTo(Value.newBuilder().setNumberValue(10000.23).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfString_ConstructsValue() {
|
||||
assertThat(Values.of("")).isEqualTo(Value.newBuilder().setStringValue("").build());
|
||||
assertThat(Values.of("foo")).isEqualTo(Value.newBuilder().setStringValue("foo").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfStruct_ConstructsValue() {
|
||||
Struct.Builder builder = Struct.newBuilder();
|
||||
builder.putFields("a", Values.of("a"));
|
||||
builder.putFields("b", Values.of("b"));
|
||||
|
||||
assertThat(Values.of(builder.build()))
|
||||
.isEqualTo(Value.newBuilder().setStructValue(builder.build()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfListValue_ConstructsInstance() {
|
||||
ListValue.Builder builder = ListValue.newBuilder();
|
||||
builder.addValues(Values.of(1));
|
||||
builder.addValues(Values.of(2));
|
||||
|
||||
assertThat(Values.of(builder.build()))
|
||||
.isEqualTo(Value.newBuilder().setListValue(builder.build()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfIterable_ReturnsTheValue() {
|
||||
ListValue.Builder builder = ListValue.newBuilder();
|
||||
builder.addValues(Values.of(1));
|
||||
builder.addValues(Values.of(2));
|
||||
builder.addValues(Values.of(true));
|
||||
builder.addValues(Value.newBuilder().setListValue(builder.build()).build());
|
||||
|
||||
List<Value> list = new ArrayList<>();
|
||||
list.add(Values.of(1));
|
||||
list.add(Values.of(2));
|
||||
list.add(Values.of(true));
|
||||
List<Value> copyList = new ArrayList<>(list);
|
||||
list.add(Values.of(copyList));
|
||||
|
||||
assertThat(Values.of(list)).isEqualTo(Value.newBuilder().setListValue(builder).build());
|
||||
assertThat(Values.of(new ArrayList<Value>()))
|
||||
.isEqualTo(Value.newBuilder().setListValue(ListValue.getDefaultInstance()).build());
|
||||
}
|
||||
}
|
190
deps/protobuf/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
vendored
Normal file
190
deps/protobuf/java/util/src/test/proto/com/google/protobuf/util/json_test.proto
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
// 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;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
|
||||
option java_package = "com.google.protobuf.util.proto";
|
||||
option java_outer_classname = "JsonTestProto";
|
||||
|
||||
message TestAllTypes {
|
||||
enum NestedEnum {
|
||||
FOO = 0;
|
||||
BAR = 1;
|
||||
BAZ = 2;
|
||||
}
|
||||
|
||||
enum AliasedEnum {
|
||||
option allow_alias = true;
|
||||
|
||||
ALIAS_FOO = 0;
|
||||
ALIAS_BAR = 1;
|
||||
ALIAS_BAZ = 2;
|
||||
QUX = 2;
|
||||
qux = 2;
|
||||
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;
|
||||
AliasedEnum optional_aliased_enum = 52;
|
||||
|
||||
// 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 TestOneof {
|
||||
oneof oneof_field {
|
||||
int32 oneof_int32 = 1;
|
||||
TestAllTypes.NestedMessage oneof_nested_message = 2;
|
||||
google.protobuf.NullValue oneof_null_value = 3;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
google.protobuf.Value value = 2;
|
||||
google.protobuf.ListValue list_value = 3;
|
||||
}
|
||||
|
||||
message TestAny {
|
||||
google.protobuf.Any any_value = 1;
|
||||
map<string, google.protobuf.Any> any_map = 2;
|
||||
}
|
||||
|
||||
message TestCustomJsonName {
|
||||
int32 value = 1 [json_name = "@value"];
|
||||
}
|
||||
|
||||
message TestRecursive {
|
||||
int32 value = 1;
|
||||
TestRecursive nested = 2;
|
||||
}
|
Reference in New Issue
Block a user