mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Squashed 'src/cpp/protobuf/' content from commit 29edac6
git-subtree-dir: src/cpp/protobuf git-subtree-split: 29edac6e7d78bdb050ed47adc79c9ac1ba22071e
This commit is contained in:
96
java/README.txt
Normal file
96
java/README.txt
Normal file
@@ -0,0 +1,96 @@
|
||||
Protocol Buffers - Google's data interchange format
|
||||
Copyright 2008 Google Inc.
|
||||
|
||||
This directory contains the Java Protocol Buffers runtime library.
|
||||
|
||||
Installation - With Maven
|
||||
=========================
|
||||
|
||||
The Protocol Buffers build is managed using Maven. If you would
|
||||
rather build without Maven, see below.
|
||||
|
||||
1) Install Apache Maven if you don't have it:
|
||||
|
||||
http://maven.apache.org/
|
||||
|
||||
2) Build the C++ code, or obtain a binary distribution of protoc. If
|
||||
you install a binary distribution, make sure that it is the same
|
||||
version as this package. If in doubt, run:
|
||||
|
||||
$ protoc --version
|
||||
|
||||
You will need to place the protoc executable in ../src. (If you
|
||||
built it yourself, it should already be there.)
|
||||
|
||||
3) Run the tests:
|
||||
|
||||
$ mvn test
|
||||
|
||||
If some tests fail, this library may not work correctly on your
|
||||
system. Continue at your own risk.
|
||||
|
||||
4) Install the library into your Maven repository:
|
||||
|
||||
$ mvn install
|
||||
|
||||
5) If you do not use Maven to manage your own build, you can build a
|
||||
.jar file to use:
|
||||
|
||||
$ mvn package
|
||||
|
||||
The .jar will be placed in the "target" directory.
|
||||
|
||||
Installation - 'Lite' Version - With Maven
|
||||
==========================================
|
||||
|
||||
Building the 'lite' version of the Java Protocol Buffers library is
|
||||
the same as building the full version, except that all commands are
|
||||
run using the 'lite' profile. (see
|
||||
http://maven.apache.org/guides/introduction/introduction-to-profiles.html)
|
||||
|
||||
E.g. to install the lite version of the jar, you would run:
|
||||
|
||||
$ mvn install -P lite
|
||||
|
||||
The resulting artifact has the 'lite' classifier. To reference it
|
||||
for dependency resolution, you would specify it as:
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>${version}</version>
|
||||
<classifier>lite</classifier>
|
||||
</dependency>
|
||||
|
||||
Installation - Without Maven
|
||||
============================
|
||||
|
||||
If you would rather not install Maven to build the library, you may
|
||||
follow these instructions instead. Note that these instructions skip
|
||||
running unit tests.
|
||||
|
||||
1) Build the C++ code, or obtain a binary distribution of protoc. If
|
||||
you install a binary distribution, make sure that it is the same
|
||||
version as this package. If in doubt, run:
|
||||
|
||||
$ protoc --version
|
||||
|
||||
If you built the C++ code without installing, the compiler binary
|
||||
should be located in ../src.
|
||||
|
||||
2) Invoke protoc to build DescriptorProtos.java:
|
||||
|
||||
$ protoc --java_out=src/main/java -I../src \
|
||||
../src/google/protobuf/descriptor.proto
|
||||
|
||||
3) Compile the code in src/main/java using whatever means you prefer.
|
||||
|
||||
4) Install the classes wherever you prefer.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
The complete documentation for Protocol Buffers is available via the
|
||||
web at:
|
||||
|
||||
http://code.google.com/apis/protocolbuffers/
|
||||
207
java/pom.xml
Normal file
207
java/pom.xml
Normal file
@@ -0,0 +1,207 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>google</artifactId>
|
||||
<version>1</version>
|
||||
</parent>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>2.5.1-pre</version>
|
||||
<packaging>bundle</packaging>
|
||||
<name>Protocol Buffer Java API</name>
|
||||
<description>
|
||||
Protocol Buffers are a way of encoding structured data in an efficient yet
|
||||
extensible format.
|
||||
</description>
|
||||
<inceptionYear>2008</inceptionYear>
|
||||
<url>http://code.google.com/p/protobuf</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>New BSD license</name>
|
||||
<url>http://www.opensource.org/licenses/bsd-license.php</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>http://code.google.com/p/protobuf/source/browse</url>
|
||||
<connection>
|
||||
scm:svn:http://protobuf.googlecode.com/svn/trunk/
|
||||
</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<version>2.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymockclassextension</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-sources</id>
|
||||
<phase>generate-sources</phase>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<mkdir dir="target/generated-sources" />
|
||||
<exec executable="../src/protoc">
|
||||
<arg value="--java_out=target/generated-sources" />
|
||||
<arg value="--proto_path=../src" />
|
||||
<arg value="../src/google/protobuf/descriptor.proto" />
|
||||
</exec>
|
||||
</tasks>
|
||||
<sourceRoot>target/generated-sources</sourceRoot>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>generate-test-sources</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<mkdir dir="target/generated-test-sources" />
|
||||
<exec executable="../src/protoc">
|
||||
<arg value="--java_out=target/generated-test-sources" />
|
||||
<arg value="--proto_path=../src" />
|
||||
<arg value="--proto_path=src/test/java" />
|
||||
<arg value="../src/google/protobuf/unittest.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import_public.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_mset.proto" />
|
||||
<arg
|
||||
value="src/test/java/com/google/protobuf/multiple_files_test.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/nested_builders_test.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/nested_extension.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/nested_extension_lite.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/non_nested_extension.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/non_nested_extension_lite.proto" />
|
||||
<arg value="src/test/java/com/google/protobuf/test_bad_identifiers.proto" />
|
||||
<arg
|
||||
value="../src/google/protobuf/unittest_optimize_for.proto" />
|
||||
<arg
|
||||
value="../src/google/protobuf/unittest_custom_options.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_lite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import_lite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_import_public_lite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_lite_imports_nonlite.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_enormous_descriptor.proto" />
|
||||
<arg value="../src/google/protobuf/unittest_no_generic_services.proto" />
|
||||
</exec>
|
||||
</tasks>
|
||||
<testSourceRoot>target/generated-test-sources</testSourceRoot>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-DocURL>http://code.google.com/p/protobuf</Bundle-DocURL>
|
||||
<Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName>
|
||||
<Export-Package>com.google.protobuf;version=2.5.0</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>lite</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/AbstractMessageLite.java</include>
|
||||
<include>**/ByteString.java</include>
|
||||
<include>**/CodedInputStream.java</include>
|
||||
<include>**/CodedOutputStream.java</include>
|
||||
<include>**/ExtensionRegistryLite.java</include>
|
||||
<include>**/FieldSet.java</include>
|
||||
<include>**/GeneratedMessageLite.java</include>
|
||||
<include>**/Internal.java</include>
|
||||
<include>**/InvalidProtocolBufferException.java</include>
|
||||
<include>**/LazyStringArrayList.java</include>
|
||||
<include>**/LazyStringList.java</include>
|
||||
<include>**/MessageLite.java</include>
|
||||
<include>**/MessageLiteOrBuilder.java</include>
|
||||
<include>**/SmallSortedMap.java</include>
|
||||
<include>**/UninitializedMessageException.java</include>
|
||||
<include>**/UnmodifiableLazyStringList.java</include>
|
||||
<include>**/WireFormat.java</include>
|
||||
<include>**/Parser.java</include>
|
||||
<include>**/AbstractParser.java</include>
|
||||
<include>**/BoundedByteString.java</include>
|
||||
<include>**/LiteralByteString.java</include>
|
||||
<include>**/RopeByteString.java</include>
|
||||
<include>**/Utf8.java</include>
|
||||
<include>**/LazyField.java</include>
|
||||
</includes>
|
||||
<testIncludes>
|
||||
<testInclude>**/LiteTest.java</testInclude>
|
||||
<testInclude>**/*Lite.java</testInclude>
|
||||
</testIncludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/LiteTest.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>lite</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
930
java/src/main/java/com/google/protobuf/AbstractMessage.java
Normal file
930
java/src/main/java/com/google/protobuf/AbstractMessage.java
Normal file
@@ -0,0 +1,930 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.GeneratedMessage.ExtendableBuilder;
|
||||
import com.google.protobuf.Internal.EnumLite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message} interface which implements
|
||||
* as many methods of that interface as possible in terms of other methods.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class AbstractMessage extends AbstractMessageLite
|
||||
implements Message {
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isInitialized() {
|
||||
// Check that all required fields are present.
|
||||
for (final FieldDescriptor field : getDescriptorForType().getFields()) {
|
||||
if (field.isRequired()) {
|
||||
if (!hasField(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that embedded messages are initialized.
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
for (final Message element : (List<Message>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!((Message) entry.getValue()).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> findInitializationErrors() {
|
||||
return Builder.findMissingFields(this);
|
||||
}
|
||||
|
||||
public String getInitializationErrorString() {
|
||||
return delimitWithCommas(findInitializationErrors());
|
||||
}
|
||||
|
||||
private static String delimitWithCommas(List<String> parts) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String part : parts) {
|
||||
if (result.length() > 0) {
|
||||
result.append(", ");
|
||||
}
|
||||
result.append(part);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return TextFormat.printToString(this);
|
||||
}
|
||||
|
||||
public void writeTo(final CodedOutputStream output) throws IOException {
|
||||
final boolean isMessageSet =
|
||||
getDescriptorForType().getOptions().getMessageSetWireFormat();
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (isMessageSet && field.isExtension() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
!field.isRepeated()) {
|
||||
output.writeMessageSetExtension(field.getNumber(), (Message) value);
|
||||
} else {
|
||||
FieldSet.writeField(field, value, output);
|
||||
}
|
||||
}
|
||||
|
||||
final UnknownFieldSet unknownFields = getUnknownFields();
|
||||
if (isMessageSet) {
|
||||
unknownFields.writeAsMessageSetTo(output);
|
||||
} else {
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
private int memoizedSize = -1;
|
||||
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) {
|
||||
return size;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
final boolean isMessageSet =
|
||||
getDescriptorForType().getOptions().getMessageSetWireFormat();
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
if (isMessageSet && field.isExtension() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
!field.isRepeated()) {
|
||||
size += CodedOutputStream.computeMessageSetExtensionSize(
|
||||
field.getNumber(), (Message) value);
|
||||
} else {
|
||||
size += FieldSet.computeFieldSize(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
final UnknownFieldSet unknownFields = getUnknownFields();
|
||||
if (isMessageSet) {
|
||||
size += unknownFields.getSerializedSizeAsMessageSet();
|
||||
} else {
|
||||
size += unknownFields.getSerializedSize();
|
||||
}
|
||||
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Message)) {
|
||||
return false;
|
||||
}
|
||||
final Message otherMessage = (Message) other;
|
||||
if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
|
||||
return false;
|
||||
}
|
||||
return getAllFields().equals(otherMessage.getAllFields()) &&
|
||||
getUnknownFields().equals(otherMessage.getUnknownFields());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 41;
|
||||
hash = (19 * hash) + getDescriptorForType().hashCode();
|
||||
hash = hashFields(hash, getAllFields());
|
||||
hash = (29 * hash) + getUnknownFields().hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/** Get a hash code for given fields and values, using the given seed. */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected int hashFields(int hash, Map<FieldDescriptor, Object> map) {
|
||||
for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) {
|
||||
FieldDescriptor field = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
hash = (37 * hash) + field.getNumber();
|
||||
if (field.getType() != FieldDescriptor.Type.ENUM){
|
||||
hash = (53 * hash) + value.hashCode();
|
||||
} else if (field.isRepeated()) {
|
||||
List<? extends EnumLite> list = (List<? extends EnumLite>) value;
|
||||
hash = (53 * hash) + hashEnumList(list);
|
||||
} else {
|
||||
hash = (53 * hash) + hashEnum((EnumLite) value);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* @see Boolean#hashCode()
|
||||
*/
|
||||
protected static int hashLong(long n) {
|
||||
return (int) (n ^ (n >>> 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* @see Boolean#hashCode()
|
||||
*/
|
||||
protected static int hashBoolean(boolean b) {
|
||||
return b ? 1231 : 1237;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private helper method for AbstractParser to create
|
||||
* UninitializedMessageException with missing field information.
|
||||
*/
|
||||
@Override
|
||||
UninitializedMessageException newUninitializedMessageException() {
|
||||
return Builder.newUninitializedMessageException(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for implementing {@link Message#hashCode()}.
|
||||
* <p>
|
||||
* This is needed because {@link java.lang.Enum#hashCode()} is final, but we
|
||||
* need to use the field number as the hash code to ensure compatibility
|
||||
* between statically and dynamically generated enum objects.
|
||||
*/
|
||||
protected static int hashEnum(EnumLite e) {
|
||||
return e.getNumber();
|
||||
}
|
||||
|
||||
/** Helper method for implementing {@link Message#hashCode()}. */
|
||||
protected static int hashEnumList(List<? extends EnumLite> list) {
|
||||
int hash = 1;
|
||||
for (EnumLite e : list) {
|
||||
hash = 31 * hash + hashEnum(e);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message.Builder} interface which
|
||||
* implements as many methods of that interface as possible in terms of
|
||||
* other methods.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static abstract class Builder<BuilderType extends Builder>
|
||||
extends AbstractMessageLite.Builder<BuilderType>
|
||||
implements Message.Builder {
|
||||
// The compiler produces an error if this is not declared explicitly.
|
||||
@Override
|
||||
public abstract BuilderType clone();
|
||||
|
||||
public BuilderType clear() {
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
getAllFields().entrySet()) {
|
||||
clearField(entry.getKey());
|
||||
}
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public List<String> findInitializationErrors() {
|
||||
return findMissingFields(this);
|
||||
}
|
||||
|
||||
public String getInitializationErrorString() {
|
||||
return delimitWithCommas(findInitializationErrors());
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final Message other) {
|
||||
if (other.getDescriptorForType() != getDescriptorForType()) {
|
||||
throw new IllegalArgumentException(
|
||||
"mergeFrom(Message) can only merge messages of the same type.");
|
||||
}
|
||||
|
||||
// Note: We don't attempt to verify that other's fields have valid
|
||||
// types. Doing so would be a losing battle. We'd have to verify
|
||||
// all sub-messages as well, and we'd have to make copies of all of
|
||||
// them to insure that they don't change after verification (since
|
||||
// the Message interface itself cannot enforce immutability of
|
||||
// implementations).
|
||||
// TODO(kenton): Provide a function somewhere called makeDeepCopy()
|
||||
// which allows people to make secure deep copies of messages.
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
other.getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
if (field.isRepeated()) {
|
||||
for (final Object element : (List)entry.getValue()) {
|
||||
addRepeatedField(field, element);
|
||||
}
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
final Message existingValue = (Message)getField(field);
|
||||
if (existingValue == existingValue.getDefaultInstanceForType()) {
|
||||
setField(field, entry.getValue());
|
||||
} else {
|
||||
setField(field,
|
||||
existingValue.newBuilderForType()
|
||||
.mergeFrom(existingValue)
|
||||
.mergeFrom((Message)entry.getValue())
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
setField(field, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
mergeUnknownFields(other.getUnknownFields());
|
||||
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return mergeFrom(input, ExtensionRegistry.getEmptyRegistry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final UnknownFieldSet.Builder unknownFields =
|
||||
UnknownFieldSet.newBuilder(getUnknownFields());
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
|
||||
getDescriptorForType(), this, null, tag)) {
|
||||
// end group tag
|
||||
break;
|
||||
}
|
||||
}
|
||||
setUnknownFields(unknownFields.build());
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void addRepeatedField(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Object value) {
|
||||
if (builder != null) {
|
||||
builder.addRepeatedField(field, value);
|
||||
} else {
|
||||
extensions.addRepeatedField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void setField(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Object value) {
|
||||
if (builder != null) {
|
||||
builder.setField(field, value);
|
||||
} else {
|
||||
extensions.setField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static boolean hasOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field) {
|
||||
if (builder != null) {
|
||||
return builder.hasField(field);
|
||||
} else {
|
||||
return extensions.hasField(field);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static Message getOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field) {
|
||||
if (builder != null) {
|
||||
return (Message) builder.getField(field);
|
||||
} else {
|
||||
return (Message) extensions.getField(field);
|
||||
}
|
||||
}
|
||||
|
||||
/** helper method to handle {@code builder} and {@code extensions}. */
|
||||
private static void mergeOriginalMessage(
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
FieldDescriptor field,
|
||||
Message.Builder subBuilder) {
|
||||
Message originalMessage = getOriginalMessage(builder, extensions, field);
|
||||
if (originalMessage != null) {
|
||||
subBuilder.mergeFrom(originalMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #mergeFrom(CodedInputStream, ExtensionRegistryLite)}, but
|
||||
* parses a single field.
|
||||
*
|
||||
* When {@code builder} is not null, the method will parse and merge the
|
||||
* field into {@code builder}. Otherwise, it will try to parse the field
|
||||
* into {@code extensions}, when it's called by the parsing constructor in
|
||||
* generated classes.
|
||||
*
|
||||
* Package-private because it is used by GeneratedMessage.ExtendableMessage.
|
||||
* @param tag The tag, which should have already been read.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
static boolean mergeFieldFrom(
|
||||
CodedInputStream input,
|
||||
UnknownFieldSet.Builder unknownFields,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Descriptor type,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions,
|
||||
int tag) throws IOException {
|
||||
if (type.getOptions().getMessageSetWireFormat() &&
|
||||
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
|
||||
mergeMessageSetExtensionFromCodedStream(
|
||||
input, unknownFields, extensionRegistry, type, builder, extensions);
|
||||
return true;
|
||||
}
|
||||
|
||||
final int wireType = WireFormat.getTagWireType(tag);
|
||||
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
final FieldDescriptor field;
|
||||
Message defaultInstance = null;
|
||||
|
||||
if (type.isExtensionNumber(fieldNumber)) {
|
||||
// extensionRegistry may be either ExtensionRegistry or
|
||||
// ExtensionRegistryLite. Since the type we are parsing is a full
|
||||
// message, only a full ExtensionRegistry could possibly contain
|
||||
// extensions of it. Otherwise we will treat the registry as if it
|
||||
// were empty.
|
||||
if (extensionRegistry instanceof ExtensionRegistry) {
|
||||
final ExtensionRegistry.ExtensionInfo extension =
|
||||
((ExtensionRegistry) extensionRegistry)
|
||||
.findExtensionByNumber(type, fieldNumber);
|
||||
if (extension == null) {
|
||||
field = null;
|
||||
} else {
|
||||
field = extension.descriptor;
|
||||
defaultInstance = extension.defaultInstance;
|
||||
if (defaultInstance == null &&
|
||||
field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalStateException(
|
||||
"Message-typed extension lacked default instance: " +
|
||||
field.getFullName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
} else if (builder != null) {
|
||||
field = type.findFieldByNumber(fieldNumber);
|
||||
} else {
|
||||
field = null;
|
||||
}
|
||||
|
||||
boolean unknown = false;
|
||||
boolean packed = false;
|
||||
if (field == null) {
|
||||
unknown = true; // Unknown field.
|
||||
} else if (wireType == FieldSet.getWireFormatForFieldType(
|
||||
field.getLiteType(),
|
||||
false /* isPacked */)) {
|
||||
packed = false;
|
||||
} else if (field.isPackable() &&
|
||||
wireType == FieldSet.getWireFormatForFieldType(
|
||||
field.getLiteType(),
|
||||
true /* isPacked */)) {
|
||||
packed = true;
|
||||
} else {
|
||||
unknown = true; // Unknown wire type.
|
||||
}
|
||||
|
||||
if (unknown) { // Unknown field or wrong wire type. Skip.
|
||||
return unknownFields.mergeFieldFrom(tag, input);
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
final int length = input.readRawVarint32();
|
||||
final int limit = input.pushLimit(length);
|
||||
if (field.getLiteType() == WireFormat.FieldType.ENUM) {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
final int rawValue = input.readEnum();
|
||||
final Object value = field.getEnumType().findValueByNumber(rawValue);
|
||||
if (value == null) {
|
||||
// If the number isn't recognized as a valid value for this
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
}
|
||||
} else {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
final Object value =
|
||||
FieldSet.readPrimitiveField(input, field.getLiteType());
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
}
|
||||
}
|
||||
input.popLimit(limit);
|
||||
} else {
|
||||
final Object value;
|
||||
switch (field.getType()) {
|
||||
case GROUP: {
|
||||
final Message.Builder subBuilder;
|
||||
if (defaultInstance != null) {
|
||||
subBuilder = defaultInstance.newBuilderForType();
|
||||
} else {
|
||||
subBuilder = builder.newBuilderForField(field);
|
||||
}
|
||||
if (!field.isRepeated()) {
|
||||
mergeOriginalMessage(builder, extensions, field, subBuilder);
|
||||
}
|
||||
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
break;
|
||||
}
|
||||
case MESSAGE: {
|
||||
final Message.Builder subBuilder;
|
||||
if (defaultInstance != null) {
|
||||
subBuilder = defaultInstance.newBuilderForType();
|
||||
} else {
|
||||
subBuilder = builder.newBuilderForField(field);
|
||||
}
|
||||
if (!field.isRepeated()) {
|
||||
mergeOriginalMessage(builder, extensions, field, subBuilder);
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
break;
|
||||
}
|
||||
case ENUM:
|
||||
final int rawValue = input.readEnum();
|
||||
value = field.getEnumType().findValueByNumber(rawValue);
|
||||
// If the number isn't recognized as a valid value for this enum,
|
||||
// drop it.
|
||||
if (value == null) {
|
||||
unknownFields.mergeVarintField(fieldNumber, rawValue);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = FieldSet.readPrimitiveField(input, field.getLiteType());
|
||||
break;
|
||||
}
|
||||
|
||||
if (field.isRepeated()) {
|
||||
addRepeatedField(builder, extensions, field, value);
|
||||
} else {
|
||||
setField(builder, extensions, field, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@code #mergeFieldFrom()} to parse a MessageSet extension.
|
||||
* If {@code builder} is not null, this method will merge MessageSet into
|
||||
* the builder. Otherwise, it will merge the MessageSet into {@code
|
||||
* extensions}.
|
||||
*/
|
||||
private static void mergeMessageSetExtensionFromCodedStream(
|
||||
CodedInputStream input,
|
||||
UnknownFieldSet.Builder unknownFields,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Descriptor type,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
// The wire format for MessageSet is:
|
||||
// message MessageSet {
|
||||
// repeated group Item = 1 {
|
||||
// required int32 typeId = 2;
|
||||
// required bytes message = 3;
|
||||
// }
|
||||
// }
|
||||
// "typeId" is the extension's field number. The extension can only be
|
||||
// a message type, where "message" contains the encoded bytes of that
|
||||
// message.
|
||||
//
|
||||
// In practice, we will probably never see a MessageSet item in which
|
||||
// the message appears before the type ID, or where either field does not
|
||||
// appear exactly once. However, in theory such cases are valid, so we
|
||||
// should be prepared to accept them.
|
||||
|
||||
int typeId = 0;
|
||||
ByteString rawBytes = null; // If we encounter "message" before "typeId"
|
||||
ExtensionRegistry.ExtensionInfo extension = null;
|
||||
|
||||
// Read bytes from input, if we get it's type first then parse it eagerly,
|
||||
// otherwise we store the raw bytes in a local variable.
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
|
||||
typeId = input.readUInt32();
|
||||
if (typeId != 0) {
|
||||
// extensionRegistry may be either ExtensionRegistry or
|
||||
// ExtensionRegistryLite. Since the type we are parsing is a full
|
||||
// message, only a full ExtensionRegistry could possibly contain
|
||||
// extensions of it. Otherwise we will treat the registry as if it
|
||||
// were empty.
|
||||
if (extensionRegistry instanceof ExtensionRegistry) {
|
||||
extension = ((ExtensionRegistry) extensionRegistry)
|
||||
.findExtensionByNumber(type, typeId);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
|
||||
if (typeId != 0) {
|
||||
if (extension != null && ExtensionRegistryLite.isEagerlyParseMessageSets()) {
|
||||
// We already know the type, so we can parse directly from the
|
||||
// input with no copying. Hooray!
|
||||
eagerlyMergeMessageSetExtension(
|
||||
input, extension, extensionRegistry, builder, extensions);
|
||||
rawBytes = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We haven't seen a type ID yet or we want parse message lazily.
|
||||
rawBytes = input.readBytes();
|
||||
|
||||
} else { // Unknown tag. Skip it.
|
||||
if (!input.skipField(tag)) {
|
||||
break; // End of group
|
||||
}
|
||||
}
|
||||
}
|
||||
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
|
||||
|
||||
// Process the raw bytes.
|
||||
if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
|
||||
if (extension != null) { // We known the type
|
||||
mergeMessageSetExtensionFromBytes(
|
||||
rawBytes, extension, extensionRegistry, builder, extensions);
|
||||
} else { // We don't know how to parse this. Ignore it.
|
||||
if (rawBytes != null) {
|
||||
unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
|
||||
.addLengthDelimited(rawBytes).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void eagerlyMergeMessageSetExtension(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistry.ExtensionInfo extension,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
FieldDescriptor field = extension.descriptor;
|
||||
Message value = null;
|
||||
if (hasOriginalMessage(builder, extensions, field)) {
|
||||
Message originalMessage =
|
||||
getOriginalMessage(builder, extensions, field);
|
||||
Message.Builder subBuilder = originalMessage.toBuilder();
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
} else {
|
||||
value = input.readMessage(extension.defaultInstance.getParserForType(),
|
||||
extensionRegistry);
|
||||
}
|
||||
|
||||
if (builder != null) {
|
||||
builder.setField(field, value);
|
||||
} else {
|
||||
extensions.setField(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void mergeMessageSetExtensionFromBytes(
|
||||
ByteString rawBytes,
|
||||
ExtensionRegistry.ExtensionInfo extension,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
Message.Builder builder,
|
||||
FieldSet<FieldDescriptor> extensions) throws IOException {
|
||||
|
||||
FieldDescriptor field = extension.descriptor;
|
||||
boolean hasOriginalValue = hasOriginalMessage(builder, extensions, field);
|
||||
|
||||
if (hasOriginalValue || ExtensionRegistryLite.isEagerlyParseMessageSets()) {
|
||||
// If the field already exists, we just parse the field.
|
||||
Message value = null;
|
||||
if (hasOriginalValue) {
|
||||
Message originalMessage =
|
||||
getOriginalMessage(builder, extensions, field);
|
||||
Message.Builder subBuilder= originalMessage.toBuilder();
|
||||
subBuilder.mergeFrom(rawBytes, extensionRegistry);
|
||||
value = subBuilder.buildPartial();
|
||||
} else {
|
||||
value = extension.defaultInstance.getParserForType()
|
||||
.parsePartialFrom(rawBytes, extensionRegistry);
|
||||
}
|
||||
setField(builder, extensions, field, value);
|
||||
} else {
|
||||
// Use LazyField to load MessageSet lazily.
|
||||
LazyField lazyField = new LazyField(
|
||||
extension.defaultInstance, extensionRegistry, rawBytes);
|
||||
if (builder != null) {
|
||||
// TODO(xiangl): it looks like this method can only be invoked by
|
||||
// ExtendableBuilder, but I'm not sure. So I double check the type of
|
||||
// builder here. It may be useless and need more investigation.
|
||||
if (builder instanceof ExtendableBuilder) {
|
||||
builder.setField(field, lazyField);
|
||||
} else {
|
||||
builder.setField(field, lazyField.getValue());
|
||||
}
|
||||
} else {
|
||||
extensions.setField(field, lazyField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
|
||||
setUnknownFields(
|
||||
UnknownFieldSet.newBuilder(getUnknownFields())
|
||||
.mergeFrom(unknownFields)
|
||||
.build());
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public Message.Builder getFieldBuilder(final FieldDescriptor field) {
|
||||
throw new UnsupportedOperationException(
|
||||
"getFieldBuilder() called on an unsupported message type.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an UninitializedMessageException reporting missing fields in
|
||||
* the given message.
|
||||
*/
|
||||
protected static UninitializedMessageException
|
||||
newUninitializedMessageException(Message message) {
|
||||
return new UninitializedMessageException(findMissingFields(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates {@code this.missingFields} with the full "path" of each
|
||||
* missing required field in the given message.
|
||||
*/
|
||||
private static List<String> findMissingFields(
|
||||
final MessageOrBuilder message) {
|
||||
final List<String> results = new ArrayList<String>();
|
||||
findMissingFields(message, "", results);
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Recursive helper implementing {@link #findMissingFields(Message)}. */
|
||||
private static void findMissingFields(final MessageOrBuilder message,
|
||||
final String prefix,
|
||||
final List<String> results) {
|
||||
for (final FieldDescriptor field :
|
||||
message.getDescriptorForType().getFields()) {
|
||||
if (field.isRequired() && !message.hasField(field)) {
|
||||
results.add(prefix + field.getName());
|
||||
}
|
||||
}
|
||||
|
||||
for (final Map.Entry<FieldDescriptor, Object> entry :
|
||||
message.getAllFields().entrySet()) {
|
||||
final FieldDescriptor field = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
int i = 0;
|
||||
for (final Object element : (List) value) {
|
||||
findMissingFields((MessageOrBuilder) element,
|
||||
subMessagePrefix(prefix, field, i++),
|
||||
results);
|
||||
}
|
||||
} else {
|
||||
if (message.hasField(field)) {
|
||||
findMissingFields((MessageOrBuilder) value,
|
||||
subMessagePrefix(prefix, field, -1),
|
||||
results);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String subMessagePrefix(final String prefix,
|
||||
final FieldDescriptor field,
|
||||
final int index) {
|
||||
final StringBuilder result = new StringBuilder(prefix);
|
||||
if (field.isExtension()) {
|
||||
result.append('(')
|
||||
.append(field.getFullName())
|
||||
.append(')');
|
||||
} else {
|
||||
result.append(field.getName());
|
||||
}
|
||||
if (index != -1) {
|
||||
result.append('[')
|
||||
.append(index)
|
||||
.append(']');
|
||||
}
|
||||
result.append('.');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// The following definitions seem to be required in order to make javac
|
||||
// not produce weird errors like:
|
||||
//
|
||||
// java/com/google/protobuf/DynamicMessage.java:203: types
|
||||
// com.google.protobuf.AbstractMessage.Builder<
|
||||
// com.google.protobuf.DynamicMessage.Builder> and
|
||||
// com.google.protobuf.AbstractMessage.Builder<
|
||||
// com.google.protobuf.DynamicMessage.Builder> are incompatible; both
|
||||
// define mergeFrom(com.google.protobuf.ByteString), but with unrelated
|
||||
// return types.
|
||||
//
|
||||
// Strangely, these lines are only needed if javac is invoked separately
|
||||
// on AbstractMessage.java and AbstractMessageLite.java. If javac is
|
||||
// invoked on both simultaneously, it works. (Or maybe the important
|
||||
// point is whether or not DynamicMessage.java is compiled together with
|
||||
// AbstractMessageLite.java -- not sure.) I suspect this is a compiler
|
||||
// bug.
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final ByteString data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return super.mergeFrom(data, off, len, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return super.mergeFrom(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderType mergeFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
return super.mergeFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeDelimitedFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return super.mergeDelimitedFrom(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeDelimitedFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
return super.mergeDelimitedFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
343
java/src/main/java/com/google/protobuf/AbstractMessageLite.java
Normal file
343
java/src/main/java/com/google/protobuf/AbstractMessageLite.java
Normal file
@@ -0,0 +1,343 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link MessageLite} interface which
|
||||
* implements as many methods of that interface as possible in terms of other
|
||||
* methods.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class AbstractMessageLite implements MessageLite {
|
||||
public ByteString toByteString() {
|
||||
try {
|
||||
final ByteString.CodedBuilder out =
|
||||
ByteString.newCodedBuilder(getSerializedSize());
|
||||
writeTo(out.getCodedOutput());
|
||||
return out.build();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
try {
|
||||
final byte[] result = new byte[getSerializedSize()];
|
||||
final CodedOutputStream output = CodedOutputStream.newInstance(result);
|
||||
writeTo(output);
|
||||
output.checkNoSpaceLeft();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a byte array threw an IOException " +
|
||||
"(should never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(final OutputStream output) throws IOException {
|
||||
final int bufferSize =
|
||||
CodedOutputStream.computePreferredBufferSize(getSerializedSize());
|
||||
final CodedOutputStream codedOutput =
|
||||
CodedOutputStream.newInstance(output, bufferSize);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
public void writeDelimitedTo(final OutputStream output) throws IOException {
|
||||
final int serialized = getSerializedSize();
|
||||
final int bufferSize = CodedOutputStream.computePreferredBufferSize(
|
||||
CodedOutputStream.computeRawVarint32Size(serialized) + serialized);
|
||||
final CodedOutputStream codedOutput =
|
||||
CodedOutputStream.newInstance(output, bufferSize);
|
||||
codedOutput.writeRawVarint32(serialized);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private helper method for AbstractParser to create
|
||||
* UninitializedMessageException.
|
||||
*/
|
||||
UninitializedMessageException newUninitializedMessageException() {
|
||||
return new UninitializedMessageException(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Message.Builder} interface which
|
||||
* implements as many methods of that interface as possible in terms of
|
||||
* other methods.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static abstract class Builder<BuilderType extends Builder>
|
||||
implements MessageLite.Builder {
|
||||
// The compiler produces an error if this is not declared explicitly.
|
||||
@Override
|
||||
public abstract BuilderType clone();
|
||||
|
||||
public BuilderType mergeFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
// Re-defined here for return type covariance.
|
||||
public abstract BuilderType mergeFrom(
|
||||
final CodedInputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
public BuilderType mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final ByteString data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input, extensionRegistry);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return mergeFrom(data, 0, data.length);
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final byte[] data, final int off,
|
||||
final int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return mergeFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final byte[] data, final int off, final int len,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input, extensionRegistry);
|
||||
input.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(final InputStream input) throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
public BuilderType mergeFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput, extensionRegistry);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An InputStream implementations which reads from some other InputStream
|
||||
* but is limited to a particular number of bytes. Used by
|
||||
* mergeDelimitedFrom(). This is intentionally package-private so that
|
||||
* UnknownFieldSet can share it.
|
||||
*/
|
||||
static final class LimitedInputStream extends FilterInputStream {
|
||||
private int limit;
|
||||
|
||||
LimitedInputStream(InputStream in, int limit) {
|
||||
super(in);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return Math.min(super.available(), limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (limit <= 0) {
|
||||
return -1;
|
||||
}
|
||||
final int result = super.read();
|
||||
if (result >= 0) {
|
||||
--limit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] b, final int off, int len)
|
||||
throws IOException {
|
||||
if (limit <= 0) {
|
||||
return -1;
|
||||
}
|
||||
len = Math.min(len, limit);
|
||||
final int result = super.read(b, off, len);
|
||||
if (result >= 0) {
|
||||
limit -= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(final long n) throws IOException {
|
||||
final long result = super.skip(Math.min(n, limit));
|
||||
if (result >= 0) {
|
||||
limit -= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(
|
||||
final InputStream input,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return false;
|
||||
}
|
||||
final int size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
final InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
mergeFrom(limitedInput, extensionRegistry);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return mergeDelimitedFrom(input,
|
||||
ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an UninitializedMessageException reporting missing fields in
|
||||
* the given message.
|
||||
*/
|
||||
protected static UninitializedMessageException
|
||||
newUninitializedMessageException(MessageLite message) {
|
||||
return new UninitializedMessageException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code values} to the {@code list}. This is a helper method
|
||||
* used by generated code. Users should ignore it.
|
||||
*
|
||||
* @throws NullPointerException if any of the elements of {@code values} is
|
||||
* null.
|
||||
*/
|
||||
protected static <T> void addAll(final Iterable<T> values,
|
||||
final Collection<? super T> list) {
|
||||
if (values instanceof LazyStringList) {
|
||||
// For StringOrByteStringLists, check the underlying elements to avoid
|
||||
// forcing conversions of ByteStrings to Strings.
|
||||
checkForNullValues(((LazyStringList) values).getUnderlyingElements());
|
||||
} else {
|
||||
checkForNullValues(values);
|
||||
}
|
||||
if (values instanceof Collection) {
|
||||
final Collection<T> collection = (Collection<T>) values;
|
||||
list.addAll(collection);
|
||||
} else {
|
||||
for (final T value : values) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkForNullValues(final Iterable<?> values) {
|
||||
for (final Object value : values) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
261
java/src/main/java/com/google/protobuf/AbstractParser.java
Normal file
261
java/src/main/java/com/google/protobuf/AbstractParser.java
Normal file
@@ -0,0 +1,261 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A partial implementation of the {@link Parser} interface which implements
|
||||
* as many methods of that interface as possible in terms of other methods.
|
||||
*
|
||||
* Note: This class implements all the convenience methods in the
|
||||
* {@link Parser} interface. See {@link Parser} for related javadocs.
|
||||
* Subclasses need to implement
|
||||
* {@link Parser#parsePartialFrom(CodedInputStream, ExtensionRegistryLite)}
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public abstract class AbstractParser<MessageType extends MessageLite>
|
||||
implements Parser<MessageType> {
|
||||
/**
|
||||
* Creates an UninitializedMessageException for MessageType.
|
||||
*/
|
||||
private UninitializedMessageException
|
||||
newUninitializedMessageException(MessageType message) {
|
||||
if (message instanceof AbstractMessageLite) {
|
||||
return ((AbstractMessageLite) message).newUninitializedMessageException();
|
||||
}
|
||||
return new UninitializedMessageException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if message is initialized.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException if it is not initialized.
|
||||
* @return The message to check.
|
||||
*/
|
||||
private MessageType checkMessageInitialized(MessageType message)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (message != null && !message.isInitialized()) {
|
||||
throw newUninitializedMessageException(message)
|
||||
.asInvalidProtocolBufferException()
|
||||
.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static final ExtensionRegistryLite EMPTY_REGISTRY
|
||||
= ExtensionRegistryLite.getEmptyRegistry();
|
||||
|
||||
public MessageType parsePartialFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
MessageType message;
|
||||
try {
|
||||
CodedInputStream input = data.newCodedInput();
|
||||
message = parsePartialFrom(input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(parsePartialFrom(data, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data, off, len);
|
||||
MessageType message = parsePartialFrom(input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, off, len, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(data, 0, data.length, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(data, off, len, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, off, len, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, 0, data.length, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(data, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
MessageType message = parsePartialFrom(codedInput, extensionRegistry);
|
||||
try {
|
||||
codedInput.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public MessageType parsePartialFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parsePartialDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
int size;
|
||||
try {
|
||||
int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return null;
|
||||
}
|
||||
size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage());
|
||||
}
|
||||
InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
return parsePartialFrom(limitedInput, extensionRegistry);
|
||||
}
|
||||
|
||||
public MessageType parsePartialDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialDelimitedFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
|
||||
public MessageType parseDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialDelimitedFrom(input, extensionRegistry));
|
||||
}
|
||||
|
||||
public MessageType parseDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseDelimitedFrom(input, EMPTY_REGISTRY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* <p>Abstract interface for a blocking RPC channel. {@code BlockingRpcChannel}
|
||||
* is the blocking equivalent to {@link RpcChannel}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author cpovirk@google.com Chris Povirk
|
||||
*/
|
||||
public interface BlockingRpcChannel {
|
||||
/**
|
||||
* Call the given method of the remote service and blocks until it returns.
|
||||
* {@code callBlockingMethod()} is the blocking equivalent to
|
||||
* {@link RpcChannel#callMethod}.
|
||||
*/
|
||||
Message callBlockingMethod(
|
||||
Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
Message responsePrototype) throws ServiceException;
|
||||
}
|
||||
64
java/src/main/java/com/google/protobuf/BlockingService.java
Normal file
64
java/src/main/java/com/google/protobuf/BlockingService.java
Normal file
@@ -0,0 +1,64 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Blocking equivalent to {@link Service}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author cpovirk@google.com Chris Povirk
|
||||
*/
|
||||
public interface BlockingService {
|
||||
/**
|
||||
* Equivalent to {@link Service#getDescriptorForType}.
|
||||
*/
|
||||
Descriptors.ServiceDescriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#callMethod}, except that
|
||||
* {@code callBlockingMethod()} returns the result of the RPC or throws a
|
||||
* {@link ServiceException} if there is a failure, rather than passing the
|
||||
* information to a callback.
|
||||
*/
|
||||
Message callBlockingMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request) throws ServiceException;
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#getRequestPrototype}.
|
||||
*/
|
||||
Message getRequestPrototype(Descriptors.MethodDescriptor method);
|
||||
|
||||
/**
|
||||
* Equivalent to {@link Service#getResponsePrototype}.
|
||||
*/
|
||||
Message getResponsePrototype(Descriptors.MethodDescriptor method);
|
||||
}
|
||||
163
java/src/main/java/com/google/protobuf/BoundedByteString.java
Normal file
163
java/src/main/java/com/google/protobuf/BoundedByteString.java
Normal file
@@ -0,0 +1,163 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class is used to represent the substring of a {@link ByteString} over a
|
||||
* single byte array. In terms of the public API of {@link ByteString}, you end
|
||||
* up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link
|
||||
* ByteString#substring(int, int)}.
|
||||
*
|
||||
* <p>This class contains most of the overhead involved in creating a substring
|
||||
* from a {@link LiteralByteString}. The overhead involves some range-checking
|
||||
* and two extra fields.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class BoundedByteString extends LiteralByteString {
|
||||
|
||||
private final int bytesOffset;
|
||||
private final int bytesLength;
|
||||
|
||||
/**
|
||||
* Creates a {@code BoundedByteString} backed by the sub-range of given array,
|
||||
* without copying.
|
||||
*
|
||||
* @param bytes array to wrap
|
||||
* @param offset index to first byte to use in bytes
|
||||
* @param length number of bytes to use from bytes
|
||||
* @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0},
|
||||
* or if {@code offset + length >
|
||||
* bytes.length}.
|
||||
*/
|
||||
BoundedByteString(byte[] bytes, int offset, int length) {
|
||||
super(bytes);
|
||||
if (offset < 0) {
|
||||
throw new IllegalArgumentException("Offset too small: " + offset);
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length too small: " + offset);
|
||||
}
|
||||
if ((long) offset + length > bytes.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Offset+Length too large: " + offset + "+" + length);
|
||||
}
|
||||
|
||||
this.bytesOffset = offset;
|
||||
this.bytesLength = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index.
|
||||
* Throws {@link ArrayIndexOutOfBoundsException}
|
||||
* for backwards-compatibility reasons although it would more properly be
|
||||
* {@link IndexOutOfBoundsException}.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
// We must check the index ourselves as we cannot rely on Java array index
|
||||
// checking for substrings.
|
||||
if (index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException("Index too small: " + index);
|
||||
}
|
||||
if (index >= size()) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Index too large: " + index + ", " + size());
|
||||
}
|
||||
|
||||
return bytes[bytesOffset + index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bytesLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOffsetIntoBytes() {
|
||||
return bytesOffset;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target,
|
||||
targetOffset, numberToCopy);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new BoundedByteIterator();
|
||||
}
|
||||
|
||||
private class BoundedByteIterator implements ByteIterator {
|
||||
|
||||
private int position;
|
||||
private final int limit;
|
||||
|
||||
private BoundedByteIterator() {
|
||||
position = getOffsetIntoBytes();
|
||||
limit = position + size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position < limit);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
// Boxing calls Byte.valueOf(byte), which does not instantiate.
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
if (position >= limit) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return bytes[position++];
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
970
java/src/main/java/com/google/protobuf/ByteString.java
Normal file
970
java/src/main/java/com/google/protobuf/ByteString.java
Normal file
@@ -0,0 +1,970 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Immutable sequence of bytes. Substring is supported by sharing the reference
|
||||
* to the immutable underlying bytes, as with {@link String}. Concatenation is
|
||||
* likewise supported without copying (long strings) by building a tree of
|
||||
* pieces in {@link RopeByteString}.
|
||||
* <p>
|
||||
* Like {@link String}, the contents of a {@link ByteString} can never be
|
||||
* observed to change, not even in the presence of a data race or incorrect
|
||||
* API usage in the client code.
|
||||
*
|
||||
* @author crazybob@google.com Bob Lee
|
||||
* @author kenton@google.com Kenton Varda
|
||||
* @author carlanton@google.com Carl Haverl
|
||||
* @author martinrb@google.com Martin Buchholz
|
||||
*/
|
||||
public abstract class ByteString implements Iterable<Byte> {
|
||||
|
||||
/**
|
||||
* When two strings to be concatenated have a combined length shorter than
|
||||
* this, we just copy their bytes on {@link #concat(ByteString)}.
|
||||
* The trade-off is copy size versus the overhead of creating tree nodes
|
||||
* in {@link RopeByteString}.
|
||||
*/
|
||||
static final int CONCATENATE_BY_COPY_SIZE = 128;
|
||||
|
||||
/**
|
||||
* When copying an InputStream into a ByteString with .readFrom(),
|
||||
* the chunks in the underlying rope start at 256 bytes, but double
|
||||
* each iteration up to 8192 bytes.
|
||||
*/
|
||||
static final int MIN_READ_FROM_CHUNK_SIZE = 0x100; // 256b
|
||||
static final int MAX_READ_FROM_CHUNK_SIZE = 0x2000; // 8k
|
||||
|
||||
/**
|
||||
* Empty {@code ByteString}.
|
||||
*/
|
||||
public static final ByteString EMPTY = new LiteralByteString(new byte[0]);
|
||||
|
||||
// This constructor is here to prevent subclassing outside of this package,
|
||||
ByteString() {}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index. This method should be used only for
|
||||
* random access to individual bytes. To access bytes sequentially, use the
|
||||
* {@link ByteIterator} returned by {@link #iterator()}, and call {@link
|
||||
* #substring(int, int)} first if necessary.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
public abstract byte byteAt(int index);
|
||||
|
||||
/**
|
||||
* Return a {@link ByteString.ByteIterator} over the bytes in the ByteString.
|
||||
* To avoid auto-boxing, you may get the iterator manually and call
|
||||
* {@link ByteIterator#nextByte()}.
|
||||
*
|
||||
* @return the iterator
|
||||
*/
|
||||
public abstract ByteIterator iterator();
|
||||
|
||||
/**
|
||||
* This interface extends {@code Iterator<Byte>}, so that we can return an
|
||||
* unboxed {@code byte}.
|
||||
*/
|
||||
public interface ByteIterator extends Iterator<Byte> {
|
||||
/**
|
||||
* An alternative to {@link Iterator#next()} that returns an
|
||||
* unboxed primitive {@code byte}.
|
||||
*
|
||||
* @return the next {@code byte} in the iteration
|
||||
* @throws NoSuchElementException if the iteration has no more elements
|
||||
*/
|
||||
byte nextByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes.
|
||||
*
|
||||
* @return size in bytes
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the size is {@code 0}, {@code false} otherwise.
|
||||
*
|
||||
* @return true if this is zero bytes long
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> substring
|
||||
|
||||
/**
|
||||
* Return the substring from {@code beginIndex}, inclusive, to the end of the
|
||||
* string.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @return substring sharing underlying data
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex < 0} or
|
||||
* {@code beginIndex > size()}.
|
||||
*/
|
||||
public ByteString substring(int beginIndex) {
|
||||
return substring(beginIndex, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the substring from {@code beginIndex}, inclusive, to {@code
|
||||
* endIndex}, exclusive.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
* @return substring sharing underlying data
|
||||
* @throws IndexOutOfBoundsException if {@code beginIndex < 0},
|
||||
* {@code endIndex > size()}, or {@code beginIndex > endIndex}.
|
||||
*/
|
||||
public abstract ByteString substring(int beginIndex, int endIndex);
|
||||
|
||||
/**
|
||||
* Tests if this bytestring starts with the specified prefix.
|
||||
* Similar to {@link String#startsWith(String)}
|
||||
*
|
||||
* @param prefix the prefix.
|
||||
* @return <code>true</code> if the byte sequence represented by the
|
||||
* argument is a prefix of the byte sequence represented by
|
||||
* this string; <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean startsWith(ByteString prefix) {
|
||||
return size() >= prefix.size() &&
|
||||
substring(0, prefix.size()).equals(prefix);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// byte[] -> ByteString
|
||||
|
||||
/**
|
||||
* Copies the given bytes into a {@code ByteString}.
|
||||
*
|
||||
* @param bytes source array
|
||||
* @param offset offset in source array
|
||||
* @param size number of bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes, int offset, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
System.arraycopy(bytes, offset, copy, 0, size);
|
||||
return new LiteralByteString(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given bytes into a {@code ByteString}.
|
||||
*
|
||||
* @param bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes) {
|
||||
return copyFrom(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the next {@code size} bytes from a {@code java.nio.ByteBuffer} into
|
||||
* a {@code ByteString}.
|
||||
*
|
||||
* @param bytes source buffer
|
||||
* @param size number of bytes to copy
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(ByteBuffer bytes, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
bytes.get(copy);
|
||||
return new LiteralByteString(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the remaining bytes from a {@code java.nio.ByteBuffer} into
|
||||
* a {@code ByteString}.
|
||||
*
|
||||
* @param bytes sourceBuffer
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(ByteBuffer bytes) {
|
||||
return copyFrom(bytes, bytes.remaining());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code text} into a sequence of bytes using the named charset
|
||||
* and returns the result as a {@code ByteString}.
|
||||
*
|
||||
* @param text source string
|
||||
* @param charsetName encoding to use
|
||||
* @return new {@code ByteString}
|
||||
* @throws UnsupportedEncodingException if the encoding isn't found
|
||||
*/
|
||||
public static ByteString copyFrom(String text, String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new LiteralByteString(text.getBytes(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code text} into a sequence of UTF-8 bytes and returns the
|
||||
* result as a {@code ByteString}.
|
||||
*
|
||||
* @param text source string
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFromUtf8(String text) {
|
||||
try {
|
||||
return new LiteralByteString(text.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 not supported?", e);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// InputStream -> ByteString
|
||||
|
||||
/**
|
||||
* Completely reads the given stream's bytes into a
|
||||
* {@code ByteString}, blocking if necessary until all bytes are
|
||||
* read through to the end of the stream.
|
||||
*
|
||||
* <b>Performance notes:</b> The returned {@code ByteString} is an
|
||||
* immutable tree of byte arrays ("chunks") of the stream data. The
|
||||
* first chunk is small, with subsequent chunks each being double
|
||||
* the size, up to 8K. If the caller knows the precise length of
|
||||
* the stream and wishes to avoid all unnecessary copies and
|
||||
* allocations, consider using the two-argument version of this
|
||||
* method, below.
|
||||
*
|
||||
* @param streamToDrain The source stream, which is read completely
|
||||
* but not closed.
|
||||
* @return A new {@code ByteString} which is made up of chunks of
|
||||
* various sizes, depending on the behavior of the underlying
|
||||
* stream.
|
||||
* @throws IOException IOException is thrown if there is a problem
|
||||
* reading the underlying stream.
|
||||
*/
|
||||
public static ByteString readFrom(InputStream streamToDrain)
|
||||
throws IOException {
|
||||
return readFrom(
|
||||
streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely reads the given stream's bytes into a
|
||||
* {@code ByteString}, blocking if necessary until all bytes are
|
||||
* read through to the end of the stream.
|
||||
*
|
||||
* <b>Performance notes:</b> The returned {@code ByteString} is an
|
||||
* immutable tree of byte arrays ("chunks") of the stream data. The
|
||||
* chunkSize parameter sets the size of these byte arrays. In
|
||||
* particular, if the chunkSize is precisely the same as the length
|
||||
* of the stream, unnecessary allocations and copies will be
|
||||
* avoided. Otherwise, the chunks will be of the given size, except
|
||||
* for the last chunk, which will be resized (via a reallocation and
|
||||
* copy) to contain the remainder of the stream.
|
||||
*
|
||||
* @param streamToDrain The source stream, which is read completely
|
||||
* but not closed.
|
||||
* @param chunkSize The size of the chunks in which to read the
|
||||
* stream.
|
||||
* @return A new {@code ByteString} which is made up of chunks of
|
||||
* the given size.
|
||||
* @throws IOException IOException is thrown if there is a problem
|
||||
* reading the underlying stream.
|
||||
*/
|
||||
public static ByteString readFrom(InputStream streamToDrain, int chunkSize)
|
||||
throws IOException {
|
||||
return readFrom(streamToDrain, chunkSize, chunkSize);
|
||||
}
|
||||
|
||||
// Helper method that takes the chunk size range as a parameter.
|
||||
public static ByteString readFrom(InputStream streamToDrain, int minChunkSize,
|
||||
int maxChunkSize) throws IOException {
|
||||
Collection<ByteString> results = new ArrayList<ByteString>();
|
||||
|
||||
// copy the inbound bytes into a list of chunks; the chunk size
|
||||
// grows exponentially to support both short and long streams.
|
||||
int chunkSize = minChunkSize;
|
||||
while (true) {
|
||||
ByteString chunk = readChunk(streamToDrain, chunkSize);
|
||||
if (chunk == null) {
|
||||
break;
|
||||
}
|
||||
results.add(chunk);
|
||||
chunkSize = Math.min(chunkSize * 2, maxChunkSize);
|
||||
}
|
||||
|
||||
return ByteString.copyFrom(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until a chunk of the given size can be made from the
|
||||
* stream, or EOF is reached. Calls read() repeatedly in case the
|
||||
* given stream implementation doesn't completely fill the given
|
||||
* buffer in one read() call.
|
||||
*
|
||||
* @return A chunk of the desired size, or else a chunk as large as
|
||||
* was available when end of stream was reached. Returns null if the
|
||||
* given stream had no more data in it.
|
||||
*/
|
||||
private static ByteString readChunk(InputStream in, final int chunkSize)
|
||||
throws IOException {
|
||||
final byte[] buf = new byte[chunkSize];
|
||||
int bytesRead = 0;
|
||||
while (bytesRead < chunkSize) {
|
||||
final int count = in.read(buf, bytesRead, chunkSize - bytesRead);
|
||||
if (count == -1) {
|
||||
break;
|
||||
}
|
||||
bytesRead += count;
|
||||
}
|
||||
|
||||
if (bytesRead == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return ByteString.copyFrom(buf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Multiple ByteStrings -> One ByteString
|
||||
|
||||
/**
|
||||
* Concatenate the given {@code ByteString} to this one. Short concatenations,
|
||||
* of total size smaller than {@link ByteString#CONCATENATE_BY_COPY_SIZE}, are
|
||||
* produced by copying the underlying bytes (as per Rope.java, <a
|
||||
* href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a>. In general, the concatenate involves no copying.
|
||||
*
|
||||
* @param other string to concatenate
|
||||
* @return a new {@code ByteString} instance
|
||||
*/
|
||||
public ByteString concat(ByteString other) {
|
||||
int thisSize = size();
|
||||
int otherSize = other.size();
|
||||
if ((long) thisSize + otherSize >= Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("ByteString would be too long: " +
|
||||
thisSize + "+" + otherSize);
|
||||
}
|
||||
|
||||
return RopeByteString.concatenate(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates all byte strings in the iterable and returns the result.
|
||||
* This is designed to run in O(list size), not O(total bytes).
|
||||
*
|
||||
* <p>The returned {@code ByteString} is not necessarily a unique object.
|
||||
* If the list is empty, the returned object is the singleton empty
|
||||
* {@code ByteString}. If the list has only one element, that
|
||||
* {@code ByteString} will be returned without copying.
|
||||
*
|
||||
* @param byteStrings strings to be concatenated
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(Iterable<ByteString> byteStrings) {
|
||||
Collection<ByteString> collection;
|
||||
if (!(byteStrings instanceof Collection)) {
|
||||
collection = new ArrayList<ByteString>();
|
||||
for (ByteString byteString : byteStrings) {
|
||||
collection.add(byteString);
|
||||
}
|
||||
} else {
|
||||
collection = (Collection<ByteString>) byteStrings;
|
||||
}
|
||||
ByteString result;
|
||||
if (collection.isEmpty()) {
|
||||
result = EMPTY;
|
||||
} else {
|
||||
result = balancedConcat(collection.iterator(), collection.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal function used by copyFrom(Iterable<ByteString>).
|
||||
// Create a balanced concatenation of the next "length" elements from the
|
||||
// iterable.
|
||||
private static ByteString balancedConcat(Iterator<ByteString> iterator,
|
||||
int length) {
|
||||
assert length >= 1;
|
||||
ByteString result;
|
||||
if (length == 1) {
|
||||
result = iterator.next();
|
||||
} else {
|
||||
int halfLength = length >>> 1;
|
||||
ByteString left = balancedConcat(iterator, halfLength);
|
||||
ByteString right = balancedConcat(iterator, length - halfLength);
|
||||
result = left.concat(right);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
/**
|
||||
* Copies bytes into a buffer at the given offset.
|
||||
*
|
||||
* @param target buffer to copy into
|
||||
* @param offset in the target buffer
|
||||
* @throws IndexOutOfBoundsException if the offset is negative or too large
|
||||
*/
|
||||
public void copyTo(byte[] target, int offset) {
|
||||
copyTo(target, 0, offset, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies bytes into a buffer.
|
||||
*
|
||||
* @param target buffer to copy into
|
||||
* @param sourceOffset offset within these bytes
|
||||
* @param targetOffset offset within the target buffer
|
||||
* @param numberToCopy number of bytes to copy
|
||||
* @throws IndexOutOfBoundsException if an offset or size is negative or too
|
||||
* large
|
||||
*/
|
||||
public void copyTo(byte[] target, int sourceOffset, int targetOffset,
|
||||
int numberToCopy) {
|
||||
if (sourceOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset);
|
||||
}
|
||||
if (targetOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Target offset < 0: " + targetOffset);
|
||||
}
|
||||
if (numberToCopy < 0) {
|
||||
throw new IndexOutOfBoundsException("Length < 0: " + numberToCopy);
|
||||
}
|
||||
if (sourceOffset + numberToCopy > size()) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Source end offset < 0: " + (sourceOffset + numberToCopy));
|
||||
}
|
||||
if (targetOffset + numberToCopy > target.length) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Target end offset < 0: " + (targetOffset + numberToCopy));
|
||||
}
|
||||
if (numberToCopy > 0) {
|
||||
copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal (package private) implementation of
|
||||
* @link{#copyTo(byte[],int,int,int}.
|
||||
* It assumes that all error checking has already been performed and that
|
||||
* @code{numberToCopy > 0}.
|
||||
*/
|
||||
protected abstract void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy);
|
||||
|
||||
/**
|
||||
* Copies bytes into a ByteBuffer.
|
||||
*
|
||||
* @param target ByteBuffer to copy into.
|
||||
* @throws java.nio.ReadOnlyBufferException if the {@code target} is read-only
|
||||
* @throws java.nio.BufferOverflowException if the {@code target}'s
|
||||
* remaining() space is not large enough to hold the data.
|
||||
*/
|
||||
public abstract void copyTo(ByteBuffer target);
|
||||
|
||||
/**
|
||||
* Copies bytes to a {@code byte[]}.
|
||||
*
|
||||
* @return copied bytes
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
int size = size();
|
||||
byte[] result = new byte[size];
|
||||
copyToInternal(result, 0, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte string to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public abstract void writeTo(OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
|
||||
* is equal to the contents of this byte string.
|
||||
* The result uses the same backing array as the byte string, if possible.
|
||||
*
|
||||
* @return wrapped bytes
|
||||
*/
|
||||
public abstract ByteBuffer asReadOnlyByteBuffer();
|
||||
|
||||
/**
|
||||
* Constructs a list of read-only {@code java.nio.ByteBuffer} objects
|
||||
* such that the concatenation of their contents is equal to the contents
|
||||
* of this byte string. The result uses the same backing arrays as the
|
||||
* byte string.
|
||||
* <p>
|
||||
* By returning a list, implementations of this method may be able to avoid
|
||||
* copying even when there are multiple backing arrays.
|
||||
*
|
||||
* @return a list of wrapped bytes
|
||||
*/
|
||||
public abstract List<ByteBuffer> asReadOnlyByteBufferList();
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the bytes using the
|
||||
* specified charset.
|
||||
*
|
||||
* @param charsetName encode using this charset
|
||||
* @return new string
|
||||
* @throws UnsupportedEncodingException if charset isn't recognized
|
||||
*/
|
||||
public abstract String toString(String charsetName)
|
||||
throws UnsupportedEncodingException;
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the bytes as UTF-8.
|
||||
*
|
||||
* @return new string using UTF-8 encoding
|
||||
*/
|
||||
public String toStringUtf8() {
|
||||
try {
|
||||
return toString("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 not supported?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this {@code ByteString} represents a well-formed UTF-8
|
||||
* byte sequence, such that the original bytes can be converted to a
|
||||
* String object and then round tripped back to bytes without loss.
|
||||
*
|
||||
* <p>More precisely, returns {@code true} whenever: <pre> {@code
|
||||
* Arrays.equals(byteString.toByteArray(),
|
||||
* new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method returns {@code false} for "overlong" byte sequences,
|
||||
* as well as for 3-byte sequences that would map to a surrogate
|
||||
* character, in accordance with the restricted definition of UTF-8
|
||||
* introduced in Unicode 3.1. Note that the UTF-8 decoder included in
|
||||
* Oracle's JDK has been modified to also reject "overlong" byte
|
||||
* sequences, but (as of 2011) still accepts 3-byte surrogate
|
||||
* character byte sequences.
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* @return whether the bytes in this {@code ByteString} are a
|
||||
* well-formed UTF-8 byte sequence
|
||||
*/
|
||||
public abstract boolean isValidUtf8();
|
||||
|
||||
/**
|
||||
* Tells whether the given byte sequence is a well-formed, malformed, or
|
||||
* incomplete UTF-8 byte sequence. This method accepts and returns a partial
|
||||
* state result, allowing the bytes for a complete UTF-8 byte sequence to be
|
||||
* composed from multiple {@code ByteString} segments.
|
||||
*
|
||||
* @param state either {@code 0} (if this is the initial decoding operation)
|
||||
* or the value returned from a call to a partial decoding method for the
|
||||
* previous bytes
|
||||
* @param offset offset of the first byte to check
|
||||
* @param length number of bytes to check
|
||||
*
|
||||
* @return {@code -1} if the partial byte sequence is definitely malformed,
|
||||
* {@code 0} if it is well-formed (no additional input needed), or, if the
|
||||
* byte sequence is "incomplete", i.e. apparently terminated in the middle of
|
||||
* a character, an opaque integer "state" value containing enough information
|
||||
* to decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
protected abstract int partialIsValidUtf8(int state, int offset, int length);
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object o);
|
||||
|
||||
/**
|
||||
* Return a non-zero hashCode depending only on the sequence of bytes
|
||||
* in this ByteString.
|
||||
*
|
||||
* @return hashCode value for this object
|
||||
*/
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
/**
|
||||
* Creates an {@code InputStream} which can be used to read the bytes.
|
||||
* <p>
|
||||
* The {@link InputStream} returned by this method is guaranteed to be
|
||||
* completely non-blocking. The method {@link InputStream#available()}
|
||||
* returns the number of bytes remaining in the stream. The methods
|
||||
* {@link InputStream#read(byte[]), {@link InputStream#read(byte[],int,int)}
|
||||
* and {@link InputStream#skip(long)} will read/skip as many bytes as are
|
||||
* available.
|
||||
* <p>
|
||||
* The methods in the returned {@link InputStream} might <b>not</b> be
|
||||
* thread safe.
|
||||
*
|
||||
* @return an input stream that returns the bytes of this byte string.
|
||||
*/
|
||||
public abstract InputStream newInput();
|
||||
|
||||
/**
|
||||
* Creates a {@link CodedInputStream} which can be used to read the bytes.
|
||||
* Using this is often more efficient than creating a {@link CodedInputStream}
|
||||
* that wraps the result of {@link #newInput()}.
|
||||
*
|
||||
* @return stream based on wrapped data
|
||||
*/
|
||||
public abstract CodedInputStream newCodedInput();
|
||||
|
||||
// =================================================================
|
||||
// Output stream
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output} with the given initial capacity. Call {@link
|
||||
* Output#toByteString()} to create the {@code ByteString} instance.
|
||||
* <p>
|
||||
* A {@link ByteString.Output} offers the same functionality as a
|
||||
* {@link ByteArrayOutputStream}, except that it returns a {@link ByteString}
|
||||
* rather than a {@code byte} array.
|
||||
*
|
||||
* @param initialCapacity estimate of number of bytes to be written
|
||||
* @return {@code OutputStream} for building a {@code ByteString}
|
||||
*/
|
||||
public static Output newOutput(int initialCapacity) {
|
||||
return new Output(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Output}. Call {@link Output#toByteString()} to create
|
||||
* the {@code ByteString} instance.
|
||||
* <p>
|
||||
* A {@link ByteString.Output} offers the same functionality as a
|
||||
* {@link ByteArrayOutputStream}, except that it returns a {@link ByteString}
|
||||
* rather than a {@code byte array}.
|
||||
*
|
||||
* @return {@code OutputStream} for building a {@code ByteString}
|
||||
*/
|
||||
public static Output newOutput() {
|
||||
return new Output(CONCATENATE_BY_COPY_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to
|
||||
* create the {@code ByteString} instance.
|
||||
*/
|
||||
public static final class Output extends OutputStream {
|
||||
// Implementation note.
|
||||
// The public methods of this class must be synchronized. ByteStrings
|
||||
// are guaranteed to be immutable. Without some sort of locking, it could
|
||||
// be possible for one thread to call toByteSring(), while another thread
|
||||
// is still modifying the underlying byte array.
|
||||
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
// argument passed by user, indicating initial capacity.
|
||||
private final int initialCapacity;
|
||||
// ByteStrings to be concatenated to create the result
|
||||
private final ArrayList<ByteString> flushedBuffers;
|
||||
// Total number of bytes in the ByteStrings of flushedBuffers
|
||||
private int flushedBuffersTotalBytes;
|
||||
// Current buffer to which we are writing
|
||||
private byte[] buffer;
|
||||
// Location in buffer[] to which we write the next byte.
|
||||
private int bufferPos;
|
||||
|
||||
/**
|
||||
* Creates a new ByteString output stream with the specified
|
||||
* initial capacity.
|
||||
*
|
||||
* @param initialCapacity the initial capacity of the output stream.
|
||||
*/
|
||||
Output(int initialCapacity) {
|
||||
if (initialCapacity < 0) {
|
||||
throw new IllegalArgumentException("Buffer size < 0");
|
||||
}
|
||||
this.initialCapacity = initialCapacity;
|
||||
this.flushedBuffers = new ArrayList<ByteString>();
|
||||
this.buffer = new byte[initialCapacity];
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) {
|
||||
if (bufferPos == buffer.length) {
|
||||
flushFullBuffer(1);
|
||||
}
|
||||
buffer[bufferPos++] = (byte)b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] b, int offset, int length) {
|
||||
if (length <= buffer.length - bufferPos) {
|
||||
// The bytes can fit into the current buffer.
|
||||
System.arraycopy(b, offset, buffer, bufferPos, length);
|
||||
bufferPos += length;
|
||||
} else {
|
||||
// Use up the current buffer
|
||||
int copySize = buffer.length - bufferPos;
|
||||
System.arraycopy(b, offset, buffer, bufferPos, copySize);
|
||||
offset += copySize;
|
||||
length -= copySize;
|
||||
// Flush the buffer, and get a new buffer at least big enough to cover
|
||||
// what we still need to output
|
||||
flushFullBuffer(length);
|
||||
System.arraycopy(b, offset, buffer, 0 /* count */, length);
|
||||
bufferPos = length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte string. Its size is the current size of this output
|
||||
* stream and its output has been copied to it.
|
||||
*
|
||||
* @return the current contents of this output stream, as a byte string.
|
||||
*/
|
||||
public synchronized ByteString toByteString() {
|
||||
flushLastBuffer();
|
||||
return ByteString.copyFrom(flushedBuffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement java.util.Arrays.copyOf() for jdk 1.5.
|
||||
*/
|
||||
private byte[] copyArray(byte[] buffer, int length) {
|
||||
byte[] result = new byte[length];
|
||||
System.arraycopy(buffer, 0, result, 0, Math.min(buffer.length, length));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte array output stream to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
ByteString[] cachedFlushBuffers;
|
||||
byte[] cachedBuffer;
|
||||
int cachedBufferPos;
|
||||
synchronized (this) {
|
||||
// Copy the information we need into local variables so as to hold
|
||||
// the lock for as short a time as possible.
|
||||
cachedFlushBuffers =
|
||||
flushedBuffers.toArray(new ByteString[flushedBuffers.size()]);
|
||||
cachedBuffer = buffer;
|
||||
cachedBufferPos = bufferPos;
|
||||
}
|
||||
for (ByteString byteString : cachedFlushBuffers) {
|
||||
byteString.writeTo(out);
|
||||
}
|
||||
|
||||
out.write(copyArray(cachedBuffer, cachedBufferPos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current size of the output stream.
|
||||
*
|
||||
* @return the current size of the output stream
|
||||
*/
|
||||
public synchronized int size() {
|
||||
return flushedBuffersTotalBytes + bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this stream, so that all currently accumulated output in the
|
||||
* output stream is discarded. The output stream can be used again,
|
||||
* reusing the already allocated buffer space.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
flushedBuffers.clear();
|
||||
flushedBuffersTotalBytes = 0;
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<ByteString.Output@%s size=%d>",
|
||||
Integer.toHexString(System.identityHashCode(this)), size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used by writers. The current buffer is full, and the
|
||||
* writer needs a new buffer whose size is at least the specified minimum
|
||||
* size.
|
||||
*/
|
||||
private void flushFullBuffer(int minSize) {
|
||||
flushedBuffers.add(new LiteralByteString(buffer));
|
||||
flushedBuffersTotalBytes += buffer.length;
|
||||
// We want to increase our total capacity by 50%, but as a minimum,
|
||||
// the new buffer should also at least be >= minSize and
|
||||
// >= initial Capacity.
|
||||
int newSize = Math.max(initialCapacity,
|
||||
Math.max(minSize, flushedBuffersTotalBytes >>> 1));
|
||||
buffer = new byte[newSize];
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used by {@link #toByteString()}. The current buffer may
|
||||
* or may not be full, but it needs to be flushed.
|
||||
*/
|
||||
private void flushLastBuffer() {
|
||||
if (bufferPos < buffer.length) {
|
||||
if (bufferPos > 0) {
|
||||
byte[] bufferCopy = copyArray(buffer, bufferPos);
|
||||
flushedBuffers.add(new LiteralByteString(bufferCopy));
|
||||
}
|
||||
// We reuse this buffer for further writes.
|
||||
} else {
|
||||
// Buffer is completely full. Huzzah.
|
||||
flushedBuffers.add(new LiteralByteString(buffer));
|
||||
// 99% of the time, we're not going to use this OutputStream again.
|
||||
// We set buffer to an empty byte stream so that we're handling this
|
||||
// case without wasting space. In the rare case that more writes
|
||||
// *do* occur, this empty buffer will be flushed and an appropriately
|
||||
// sized new buffer will be created.
|
||||
buffer = EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
flushedBuffersTotalBytes += bufferPos;
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ByteString} builder, which allows you to
|
||||
* efficiently construct a {@code ByteString} by writing to a {@link
|
||||
* CodedOutputStream}. Using this is much more efficient than calling {@code
|
||||
* newOutput()} and wrapping that in a {@code CodedOutputStream}.
|
||||
*
|
||||
* <p>This is package-private because it's a somewhat confusing interface.
|
||||
* Users can call {@link Message#toByteString()} instead of calling this
|
||||
* directly.
|
||||
*
|
||||
* @param size The target byte size of the {@code ByteString}. You must write
|
||||
* exactly this many bytes before building the result.
|
||||
* @return the builder
|
||||
*/
|
||||
static CodedBuilder newCodedBuilder(int size) {
|
||||
return new CodedBuilder(size);
|
||||
}
|
||||
|
||||
/** See {@link ByteString#newCodedBuilder(int)}. */
|
||||
static final class CodedBuilder {
|
||||
private final CodedOutputStream output;
|
||||
private final byte[] buffer;
|
||||
|
||||
private CodedBuilder(int size) {
|
||||
buffer = new byte[size];
|
||||
output = CodedOutputStream.newInstance(buffer);
|
||||
}
|
||||
|
||||
public ByteString build() {
|
||||
output.checkNoSpaceLeft();
|
||||
|
||||
// We can be confident that the CodedOutputStream will not modify the
|
||||
// underlying bytes anymore because it already wrote all of them. So,
|
||||
// no need to make a copy.
|
||||
return new LiteralByteString(buffer);
|
||||
}
|
||||
|
||||
public CodedOutputStream getCodedOutput() {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Methods {@link RopeByteString} needs on instances, which aren't part of the
|
||||
// public API.
|
||||
|
||||
/**
|
||||
* Return the depth of the tree representing this {@code ByteString}, if any,
|
||||
* whose root is this node. If this is a leaf node, return 0.
|
||||
*
|
||||
* @return tree depth or zero
|
||||
*/
|
||||
protected abstract int getTreeDepth();
|
||||
|
||||
/**
|
||||
* Return {@code true} if this ByteString is literal (a leaf node) or a
|
||||
* flat-enough tree in the sense of {@link RopeByteString}.
|
||||
*
|
||||
* @return true if the tree is flat enough
|
||||
*/
|
||||
protected abstract boolean isBalanced();
|
||||
|
||||
/**
|
||||
* Return the cached hash code if available.
|
||||
*
|
||||
* @return value of cached hash code or 0 if not computed yet
|
||||
*/
|
||||
protected abstract int peekCachedHashCode();
|
||||
|
||||
/**
|
||||
* Compute the hash across the value bytes starting with the given hash, and
|
||||
* return the result. This is used to compute the hash across strings
|
||||
* represented as a set of pieces by allowing the hash computation to be
|
||||
* continued from piece to piece.
|
||||
*
|
||||
* @param h starting hash value
|
||||
* @param offset offset into this value to start looking at data values
|
||||
* @param length number of data values to include in the hash computation
|
||||
* @return ending hash value
|
||||
*/
|
||||
protected abstract int partialHash(int h, int offset, int length);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<ByteString@%s size=%d>",
|
||||
Integer.toHexString(System.identityHashCode(this)), size());
|
||||
}
|
||||
}
|
||||
920
java/src/main/java/com/google/protobuf/CodedInputStream.java
Normal file
920
java/src/main/java/com/google/protobuf/CodedInputStream.java
Normal file
@@ -0,0 +1,920 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reads and decodes protocol message fields.
|
||||
*
|
||||
* This class contains two kinds of methods: methods that read specific
|
||||
* protocol message constructs and field types (e.g. {@link #readTag()} and
|
||||
* {@link #readInt32()}) and methods that read low-level values (e.g.
|
||||
* {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading
|
||||
* encoded protocol messages, you should use the former methods, but if you are
|
||||
* reading some other format of your own design, use the latter.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class CodedInputStream {
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given InputStream.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final InputStream input) {
|
||||
return new CodedInputStream(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf) {
|
||||
return newInstance(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array slice.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf, final int off,
|
||||
final int len) {
|
||||
CodedInputStream result = new CodedInputStream(buf, off, len);
|
||||
try {
|
||||
// Some uses of CodedInputStream can be more efficient if they know
|
||||
// exactly how many bytes are available. By pushing the end point of the
|
||||
// buffer as a limit, we allow them to get this information via
|
||||
// getBytesUntilLimit(). Pushing a limit that we know is at the end of
|
||||
// the stream can never hurt, since we can never past that point anyway.
|
||||
result.pushLimit(len);
|
||||
} catch (InvalidProtocolBufferException ex) {
|
||||
// The only reason pushLimit() might throw an exception here is if len
|
||||
// is negative. Normally pushLimit()'s parameter comes directly off the
|
||||
// wire, so it's important to catch exceptions in case of corrupt or
|
||||
// malicious data. However, in this case, we expect that len is not a
|
||||
// user-supplied value, so we can assume that it being negative indicates
|
||||
// a programming error. Therefore, throwing an unchecked exception is
|
||||
// appropriate.
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Attempt to read a field tag, returning zero if we have reached EOF.
|
||||
* Protocol message parsers use this to read tags, since a protocol message
|
||||
* may legally end wherever a tag occurs, and zero is not a valid tag number.
|
||||
*/
|
||||
public int readTag() throws IOException {
|
||||
if (isAtEnd()) {
|
||||
lastTag = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lastTag = readRawVarint32();
|
||||
if (WireFormat.getTagFieldNumber(lastTag) == 0) {
|
||||
// If we actually read zero (or any tag number corresponding to field
|
||||
// number zero), that's not a valid tag.
|
||||
throw InvalidProtocolBufferException.invalidTag();
|
||||
}
|
||||
return lastTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the last call to readTag() returned the given tag value.
|
||||
* This is used to verify that a nested group ended with the correct
|
||||
* end tag.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException {@code value} does not match the
|
||||
* last tag.
|
||||
*/
|
||||
public void checkLastTagWas(final int value)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (lastTag != value) {
|
||||
throw InvalidProtocolBufferException.invalidEndTag();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards a single field, given its tag value.
|
||||
*
|
||||
* @return {@code false} if the tag is an endgroup tag, in which case
|
||||
* nothing is skipped. Otherwise, returns {@code true}.
|
||||
*/
|
||||
public boolean skipField(final int tag) throws IOException {
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
readInt32();
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
readRawLittleEndian64();
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
skipRawBytes(readRawVarint32());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
skipMessage();
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
|
||||
WireFormat.WIRETYPE_END_GROUP));
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_END_GROUP:
|
||||
return false;
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
readRawLittleEndian32();
|
||||
return true;
|
||||
default:
|
||||
throw InvalidProtocolBufferException.invalidWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards an entire message. This will read either until EOF
|
||||
* or until an endgroup tag, whichever comes first.
|
||||
*/
|
||||
public void skipMessage() throws IOException {
|
||||
while (true) {
|
||||
final int tag = readTag();
|
||||
if (tag == 0 || !skipField(tag)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/** Read a {@code double} field value from the stream. */
|
||||
public double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readRawLittleEndian64());
|
||||
}
|
||||
|
||||
/** Read a {@code float} field value from the stream. */
|
||||
public float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readRawLittleEndian32());
|
||||
}
|
||||
|
||||
/** Read a {@code uint64} field value from the stream. */
|
||||
public long readUInt64() throws IOException {
|
||||
return readRawVarint64();
|
||||
}
|
||||
|
||||
/** Read an {@code int64} field value from the stream. */
|
||||
public long readInt64() throws IOException {
|
||||
return readRawVarint64();
|
||||
}
|
||||
|
||||
/** Read an {@code int32} field value from the stream. */
|
||||
public int readInt32() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/** Read a {@code fixed64} field value from the stream. */
|
||||
public long readFixed64() throws IOException {
|
||||
return readRawLittleEndian64();
|
||||
}
|
||||
|
||||
/** Read a {@code fixed32} field value from the stream. */
|
||||
public int readFixed32() throws IOException {
|
||||
return readRawLittleEndian32();
|
||||
}
|
||||
|
||||
/** Read a {@code bool} field value from the stream. */
|
||||
public boolean readBool() throws IOException {
|
||||
return readRawVarint32() != 0;
|
||||
}
|
||||
|
||||
/** Read a {@code string} field value from the stream. */
|
||||
public String readString() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
final String result = new String(buffer, bufferPos, size, "UTF-8");
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return new String(readRawBytes(size), "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a {@code group} field value from the stream. */
|
||||
public void readGroup(final int fieldNumber,
|
||||
final MessageLite.Builder builder,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
++recursionDepth;
|
||||
builder.mergeFrom(this, extensionRegistry);
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
|
||||
--recursionDepth;
|
||||
}
|
||||
|
||||
/** Read a {@code group} field value from the stream. */
|
||||
public <T extends MessageLite> T readGroup(
|
||||
final int fieldNumber,
|
||||
final Parser<T> parser,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
++recursionDepth;
|
||||
T result = parser.parsePartialFrom(this, extensionRegistry);
|
||||
checkLastTagWas(
|
||||
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
|
||||
--recursionDepth;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a {@code group} field value from the stream and merges it into the
|
||||
* given {@link UnknownFieldSet}.
|
||||
*
|
||||
* @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so
|
||||
* you can just call {@link #readGroup}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void readUnknownGroup(final int fieldNumber,
|
||||
final MessageLite.Builder builder)
|
||||
throws IOException {
|
||||
// We know that UnknownFieldSet will ignore any ExtensionRegistry so it
|
||||
// is safe to pass null here. (We can't call
|
||||
// ExtensionRegistry.getEmptyRegistry() because that would make this
|
||||
// class depend on ExtensionRegistry, which is not part of the lite
|
||||
// library.)
|
||||
readGroup(fieldNumber, builder, null);
|
||||
}
|
||||
|
||||
/** Read an embedded message field value from the stream. */
|
||||
public void readMessage(final MessageLite.Builder builder,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
final int length = readRawVarint32();
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
final int oldLimit = pushLimit(length);
|
||||
++recursionDepth;
|
||||
builder.mergeFrom(this, extensionRegistry);
|
||||
checkLastTagWas(0);
|
||||
--recursionDepth;
|
||||
popLimit(oldLimit);
|
||||
}
|
||||
|
||||
/** Read an embedded message field value from the stream. */
|
||||
public <T extends MessageLite> T readMessage(
|
||||
final Parser<T> parser,
|
||||
final ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
int length = readRawVarint32();
|
||||
if (recursionDepth >= recursionLimit) {
|
||||
throw InvalidProtocolBufferException.recursionLimitExceeded();
|
||||
}
|
||||
final int oldLimit = pushLimit(length);
|
||||
++recursionDepth;
|
||||
T result = parser.parsePartialFrom(this, extensionRegistry);
|
||||
checkLastTagWas(0);
|
||||
--recursionDepth;
|
||||
popLimit(oldLimit);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Read a {@code bytes} field value from the stream. */
|
||||
public ByteString readBytes() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
if (size == 0) {
|
||||
return ByteString.EMPTY;
|
||||
} else if (size <= (bufferSize - bufferPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return ByteString.copyFrom(readRawBytes(size));
|
||||
}
|
||||
}
|
||||
|
||||
/** Read a {@code uint32} field value from the stream. */
|
||||
public int readUInt32() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an enum field value from the stream. Caller is responsible
|
||||
* for converting the numeric value to an actual enum.
|
||||
*/
|
||||
public int readEnum() throws IOException {
|
||||
return readRawVarint32();
|
||||
}
|
||||
|
||||
/** Read an {@code sfixed32} field value from the stream. */
|
||||
public int readSFixed32() throws IOException {
|
||||
return readRawLittleEndian32();
|
||||
}
|
||||
|
||||
/** Read an {@code sfixed64} field value from the stream. */
|
||||
public long readSFixed64() throws IOException {
|
||||
return readRawLittleEndian64();
|
||||
}
|
||||
|
||||
/** Read an {@code sint32} field value from the stream. */
|
||||
public int readSInt32() throws IOException {
|
||||
return decodeZigZag32(readRawVarint32());
|
||||
}
|
||||
|
||||
/** Read an {@code sint64} field value from the stream. */
|
||||
public long readSInt64() throws IOException {
|
||||
return decodeZigZag64(readRawVarint64());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* Read a raw Varint from the stream. If larger than 32 bits, discard the
|
||||
* upper bits.
|
||||
*/
|
||||
public int readRawVarint32() throws IOException {
|
||||
byte tmp = readRawByte();
|
||||
if (tmp >= 0) {
|
||||
return tmp;
|
||||
}
|
||||
int result = tmp & 0x7f;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 7;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 7;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 14;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 14;
|
||||
if ((tmp = readRawByte()) >= 0) {
|
||||
result |= tmp << 21;
|
||||
} else {
|
||||
result |= (tmp & 0x7f) << 21;
|
||||
result |= (tmp = readRawByte()) << 28;
|
||||
if (tmp < 0) {
|
||||
// Discard upper 32 bits.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (readRawByte() >= 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a varint from the input one byte at a time, so that it does not
|
||||
* read any bytes after the end of the varint. If you simply wrapped the
|
||||
* stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
|
||||
* then you would probably end up reading past the end of the varint since
|
||||
* CodedInputStream buffers its input.
|
||||
*/
|
||||
static int readRawVarint32(final InputStream input) throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
return readRawVarint32(firstByte, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #readRawVarint32(InputStream)}, but expects that the caller
|
||||
* has already read one byte. This allows the caller to determine if EOF
|
||||
* has been reached before attempting to read.
|
||||
*/
|
||||
public static int readRawVarint32(
|
||||
final int firstByte, final InputStream input) throws IOException {
|
||||
if ((firstByte & 0x80) == 0) {
|
||||
return firstByte;
|
||||
}
|
||||
|
||||
int result = firstByte & 0x7f;
|
||||
int offset = 7;
|
||||
for (; offset < 32; offset += 7) {
|
||||
final int b = input.read();
|
||||
if (b == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
result |= (b & 0x7f) << offset;
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Keep reading up to 64 bits.
|
||||
for (; offset < 64; offset += 7) {
|
||||
final int b = input.read();
|
||||
if (b == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
|
||||
/** Read a raw Varint from the stream. */
|
||||
public long readRawVarint64() throws IOException {
|
||||
int shift = 0;
|
||||
long result = 0;
|
||||
while (shift < 64) {
|
||||
final byte b = readRawByte();
|
||||
result |= (long)(b & 0x7F) << shift;
|
||||
if ((b & 0x80) == 0) {
|
||||
return result;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
throw InvalidProtocolBufferException.malformedVarint();
|
||||
}
|
||||
|
||||
/** Read a 32-bit little-endian integer from the stream. */
|
||||
public int readRawLittleEndian32() throws IOException {
|
||||
final byte b1 = readRawByte();
|
||||
final byte b2 = readRawByte();
|
||||
final byte b3 = readRawByte();
|
||||
final byte b4 = readRawByte();
|
||||
return (((int)b1 & 0xff) ) |
|
||||
(((int)b2 & 0xff) << 8) |
|
||||
(((int)b3 & 0xff) << 16) |
|
||||
(((int)b4 & 0xff) << 24);
|
||||
}
|
||||
|
||||
/** Read a 64-bit little-endian integer from the stream. */
|
||||
public long readRawLittleEndian64() throws IOException {
|
||||
final byte b1 = readRawByte();
|
||||
final byte b2 = readRawByte();
|
||||
final byte b3 = readRawByte();
|
||||
final byte b4 = readRawByte();
|
||||
final byte b5 = readRawByte();
|
||||
final byte b6 = readRawByte();
|
||||
final byte b7 = readRawByte();
|
||||
final byte b8 = readRawByte();
|
||||
return (((long)b1 & 0xff) ) |
|
||||
(((long)b2 & 0xff) << 8) |
|
||||
(((long)b3 & 0xff) << 16) |
|
||||
(((long)b4 & 0xff) << 24) |
|
||||
(((long)b5 & 0xff) << 32) |
|
||||
(((long)b6 & 0xff) << 40) |
|
||||
(((long)b7 & 0xff) << 48) |
|
||||
(((long)b8 & 0xff) << 56);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
|
||||
* into values that can be efficiently encoded with varint. (Otherwise,
|
||||
* negative values must be sign-extended to 64 bits to be varint encoded,
|
||||
* thus always taking 10 bytes on the wire.)
|
||||
*
|
||||
* @param n An unsigned 32-bit integer, stored in a signed int because
|
||||
* Java has no explicit unsigned support.
|
||||
* @return A signed 32-bit integer.
|
||||
*/
|
||||
public static int decodeZigZag32(final int n) {
|
||||
return (n >>> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
|
||||
* into values that can be efficiently encoded with varint. (Otherwise,
|
||||
* negative values must be sign-extended to 64 bits to be varint encoded,
|
||||
* thus always taking 10 bytes on the wire.)
|
||||
*
|
||||
* @param n An unsigned 64-bit integer, stored in a signed int because
|
||||
* Java has no explicit unsigned support.
|
||||
* @return A signed 64-bit integer.
|
||||
*/
|
||||
public static long decodeZigZag64(final long n) {
|
||||
return (n >>> 1) ^ -(n & 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
private final byte[] buffer;
|
||||
private int bufferSize;
|
||||
private int bufferSizeAfterLimit;
|
||||
private int bufferPos;
|
||||
private final InputStream input;
|
||||
private int lastTag;
|
||||
|
||||
/**
|
||||
* The total number of bytes read before the current buffer. The total
|
||||
* bytes read up to the current position can be computed as
|
||||
* {@code totalBytesRetired + bufferPos}. This value may be negative if
|
||||
* reading started in the middle of the current buffer (e.g. if the
|
||||
* constructor that takes a byte array and an offset was used).
|
||||
*/
|
||||
private int totalBytesRetired;
|
||||
|
||||
/** The absolute position of the end of the current message. */
|
||||
private int currentLimit = Integer.MAX_VALUE;
|
||||
|
||||
/** See setRecursionLimit() */
|
||||
private int recursionDepth;
|
||||
private int recursionLimit = DEFAULT_RECURSION_LIMIT;
|
||||
|
||||
/** See setSizeLimit() */
|
||||
private int sizeLimit = DEFAULT_SIZE_LIMIT;
|
||||
|
||||
private static final int DEFAULT_RECURSION_LIMIT = 64;
|
||||
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private CodedInputStream(final byte[] buffer, final int off, final int len) {
|
||||
this.buffer = buffer;
|
||||
bufferSize = off + len;
|
||||
bufferPos = off;
|
||||
totalBytesRetired = -off;
|
||||
input = null;
|
||||
}
|
||||
|
||||
private CodedInputStream(final InputStream input) {
|
||||
buffer = new byte[BUFFER_SIZE];
|
||||
bufferSize = 0;
|
||||
bufferPos = 0;
|
||||
totalBytesRetired = 0;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum message recursion depth. In order to prevent malicious
|
||||
* messages from causing stack overflows, {@code CodedInputStream} limits
|
||||
* how deeply messages may be nested. The default limit is 64.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int setRecursionLimit(final int limit) {
|
||||
if (limit < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Recursion limit cannot be negative: " + limit);
|
||||
}
|
||||
final int oldLimit = recursionLimit;
|
||||
recursionLimit = limit;
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum message size. In order to prevent malicious
|
||||
* messages from exhausting memory or causing integer overflows,
|
||||
* {@code CodedInputStream} limits how large a message may be.
|
||||
* The default limit is 64MB. You should set this limit as small
|
||||
* as you can without harming your app's functionality. Note that
|
||||
* size limits only apply when reading from an {@code InputStream}, not
|
||||
* when constructed around a raw byte array (nor with
|
||||
* {@link ByteString#newCodedInput}).
|
||||
* <p>
|
||||
* If you want to read several messages from a single CodedInputStream, you
|
||||
* could call {@link #resetSizeCounter()} after each one to avoid hitting the
|
||||
* size limit.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int setSizeLimit(final int limit) {
|
||||
if (limit < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Size limit cannot be negative: " + limit);
|
||||
}
|
||||
final int oldLimit = sizeLimit;
|
||||
sizeLimit = limit;
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
|
||||
*/
|
||||
public void resetSizeCounter() {
|
||||
totalBytesRetired = -bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
|
||||
* is called when descending into a length-delimited embedded message.
|
||||
*
|
||||
* <p>Note that {@code pushLimit()} does NOT affect how many bytes the
|
||||
* {@code CodedInputStream} reads from an underlying {@code InputStream} when
|
||||
* refreshing its buffer. If you need to prevent reading past a certain
|
||||
* point in the underlying {@code InputStream} (e.g. because you expect it to
|
||||
* contain more data after the end of the message which you need to handle
|
||||
* differently) then you must place a wrapper around your {@code InputStream}
|
||||
* which limits the amount of data that can be read from it.
|
||||
*
|
||||
* @return the old limit.
|
||||
*/
|
||||
public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
|
||||
if (byteLimit < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
byteLimit += totalBytesRetired + bufferPos;
|
||||
final int oldLimit = currentLimit;
|
||||
if (byteLimit > oldLimit) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
currentLimit = byteLimit;
|
||||
|
||||
recomputeBufferSizeAfterLimit();
|
||||
|
||||
return oldLimit;
|
||||
}
|
||||
|
||||
private void recomputeBufferSizeAfterLimit() {
|
||||
bufferSize += bufferSizeAfterLimit;
|
||||
final int bufferEnd = totalBytesRetired + bufferSize;
|
||||
if (bufferEnd > currentLimit) {
|
||||
// Limit is in current buffer.
|
||||
bufferSizeAfterLimit = bufferEnd - currentLimit;
|
||||
bufferSize -= bufferSizeAfterLimit;
|
||||
} else {
|
||||
bufferSizeAfterLimit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards the current limit, returning to the previous limit.
|
||||
*
|
||||
* @param oldLimit The old limit, as returned by {@code pushLimit}.
|
||||
*/
|
||||
public void popLimit(final int oldLimit) {
|
||||
currentLimit = oldLimit;
|
||||
recomputeBufferSizeAfterLimit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes to be read before the current limit.
|
||||
* If no limit is set, returns -1.
|
||||
*/
|
||||
public int getBytesUntilLimit() {
|
||||
if (currentLimit == Integer.MAX_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final int currentAbsolutePosition = totalBytesRetired + bufferPos;
|
||||
return currentLimit - currentAbsolutePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the stream has reached the end of the input. This is the
|
||||
* case if either the end of the underlying input source has been reached or
|
||||
* if the stream has reached a limit created using {@link #pushLimit(int)}.
|
||||
*/
|
||||
public boolean isAtEnd() throws IOException {
|
||||
return bufferPos == bufferSize && !refillBuffer(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The total bytes read up to the current position. Calling
|
||||
* {@link #resetSizeCounter()} resets this value to zero.
|
||||
*/
|
||||
public int getTotalBytesRead() {
|
||||
return totalBytesRetired + bufferPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called with {@code this.buffer} is empty to read more bytes from the
|
||||
* input. If {@code mustSucceed} is true, refillBuffer() guarantees that
|
||||
* either there will be at least one byte in the buffer when it returns
|
||||
* or it will throw an exception. If {@code mustSucceed} is false,
|
||||
* refillBuffer() returns false if no more bytes were available.
|
||||
*/
|
||||
private boolean refillBuffer(final boolean mustSucceed) throws IOException {
|
||||
if (bufferPos < bufferSize) {
|
||||
throw new IllegalStateException(
|
||||
"refillBuffer() called when buffer wasn't empty.");
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferSize == currentLimit) {
|
||||
// Oops, we hit a limit.
|
||||
if (mustSucceed) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
totalBytesRetired += bufferSize;
|
||||
|
||||
bufferPos = 0;
|
||||
bufferSize = (input == null) ? -1 : input.read(buffer);
|
||||
if (bufferSize == 0 || bufferSize < -1) {
|
||||
throw new IllegalStateException(
|
||||
"InputStream#read(byte[]) returned invalid result: " + bufferSize +
|
||||
"\nThe InputStream implementation is buggy.");
|
||||
}
|
||||
if (bufferSize == -1) {
|
||||
bufferSize = 0;
|
||||
if (mustSucceed) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
recomputeBufferSizeAfterLimit();
|
||||
final int totalBytesRead =
|
||||
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
|
||||
if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
|
||||
throw InvalidProtocolBufferException.sizeLimitExceeded();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte from the input.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public byte readRawByte() throws IOException {
|
||||
if (bufferPos == bufferSize) {
|
||||
refillBuffer(true);
|
||||
}
|
||||
return buffer[bufferPos++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a fixed size of bytes from the input.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public byte[] readRawBytes(final int size) throws IOException {
|
||||
if (size < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferPos + size > currentLimit) {
|
||||
// Read to the end of the stream anyway.
|
||||
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
||||
// Then fail.
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
|
||||
if (size <= bufferSize - bufferPos) {
|
||||
// We have all the bytes we need already.
|
||||
final byte[] bytes = new byte[size];
|
||||
System.arraycopy(buffer, bufferPos, bytes, 0, size);
|
||||
bufferPos += size;
|
||||
return bytes;
|
||||
} else if (size < BUFFER_SIZE) {
|
||||
// Reading more bytes than are in the buffer, but not an excessive number
|
||||
// of bytes. We can safely allocate the resulting array ahead of time.
|
||||
|
||||
// First copy what we have.
|
||||
final byte[] bytes = new byte[size];
|
||||
int pos = bufferSize - bufferPos;
|
||||
System.arraycopy(buffer, bufferPos, bytes, 0, pos);
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// We want to use refillBuffer() and then copy from the buffer into our
|
||||
// byte array rather than reading directly into our byte array because
|
||||
// the input may be unbuffered.
|
||||
refillBuffer(true);
|
||||
|
||||
while (size - pos > bufferSize) {
|
||||
System.arraycopy(buffer, 0, bytes, pos, bufferSize);
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
System.arraycopy(buffer, 0, bytes, pos, size - pos);
|
||||
bufferPos = size - pos;
|
||||
|
||||
return bytes;
|
||||
} else {
|
||||
// The size is very large. For security reasons, we can't allocate the
|
||||
// entire byte array yet. The size comes directly from the input, so a
|
||||
// maliciously-crafted message could provide a bogus very large size in
|
||||
// order to trick the app into allocating a lot of memory. We avoid this
|
||||
// by allocating and reading only a small chunk at a time, so that the
|
||||
// malicious message must actually *be* extremely large to cause
|
||||
// problems. Meanwhile, we limit the allowed size of a message elsewhere.
|
||||
|
||||
// Remember the buffer markers since we'll have to copy the bytes out of
|
||||
// it later.
|
||||
final int originalBufferPos = bufferPos;
|
||||
final int originalBufferSize = bufferSize;
|
||||
|
||||
// Mark the current buffer consumed.
|
||||
totalBytesRetired += bufferSize;
|
||||
bufferPos = 0;
|
||||
bufferSize = 0;
|
||||
|
||||
// Read all the rest of the bytes we need.
|
||||
int sizeLeft = size - (originalBufferSize - originalBufferPos);
|
||||
final List<byte[]> chunks = new ArrayList<byte[]>();
|
||||
|
||||
while (sizeLeft > 0) {
|
||||
final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
|
||||
int pos = 0;
|
||||
while (pos < chunk.length) {
|
||||
final int n = (input == null) ? -1 :
|
||||
input.read(chunk, pos, chunk.length - pos);
|
||||
if (n == -1) {
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
totalBytesRetired += n;
|
||||
pos += n;
|
||||
}
|
||||
sizeLeft -= chunk.length;
|
||||
chunks.add(chunk);
|
||||
}
|
||||
|
||||
// OK, got everything. Now concatenate it all into one buffer.
|
||||
final byte[] bytes = new byte[size];
|
||||
|
||||
// Start by copying the leftover bytes from this.buffer.
|
||||
int pos = originalBufferSize - originalBufferPos;
|
||||
System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
|
||||
|
||||
// And now all the chunks.
|
||||
for (final byte[] chunk : chunks) {
|
||||
System.arraycopy(chunk, 0, bytes, pos, chunk.length);
|
||||
pos += chunk.length;
|
||||
}
|
||||
|
||||
// Done.
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and discards {@code size} bytes.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException The end of the stream or the current
|
||||
* limit was reached.
|
||||
*/
|
||||
public void skipRawBytes(final int size) throws IOException {
|
||||
if (size < 0) {
|
||||
throw InvalidProtocolBufferException.negativeSize();
|
||||
}
|
||||
|
||||
if (totalBytesRetired + bufferPos + size > currentLimit) {
|
||||
// Read to the end of the stream anyway.
|
||||
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
|
||||
// Then fail.
|
||||
throw InvalidProtocolBufferException.truncatedMessage();
|
||||
}
|
||||
|
||||
if (size <= bufferSize - bufferPos) {
|
||||
// We have all the bytes we need already.
|
||||
bufferPos += size;
|
||||
} else {
|
||||
// Skipping more bytes than are in the buffer. First skip what we have.
|
||||
int pos = bufferSize - bufferPos;
|
||||
bufferPos = bufferSize;
|
||||
|
||||
// Keep refilling the buffer until we get to the point we wanted to skip
|
||||
// to. This has the side effect of ensuring the limits are updated
|
||||
// correctly.
|
||||
refillBuffer(true);
|
||||
while (size - pos > bufferSize) {
|
||||
pos += bufferSize;
|
||||
bufferPos = bufferSize;
|
||||
refillBuffer(true);
|
||||
}
|
||||
|
||||
bufferPos = size - pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
1111
java/src/main/java/com/google/protobuf/CodedOutputStream.java
Normal file
1111
java/src/main/java/com/google/protobuf/CodedOutputStream.java
Normal file
File diff suppressed because it is too large
Load Diff
1970
java/src/main/java/com/google/protobuf/Descriptors.java
Normal file
1970
java/src/main/java/com/google/protobuf/Descriptors.java
Normal file
File diff suppressed because it is too large
Load Diff
482
java/src/main/java/com/google/protobuf/DynamicMessage.java
Normal file
482
java/src/main/java/com/google/protobuf/DynamicMessage.java
Normal file
@@ -0,0 +1,482 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link Message} that can represent arbitrary types,
|
||||
* given a {@link Descriptors.Descriptor}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class DynamicMessage extends AbstractMessage {
|
||||
private final Descriptor type;
|
||||
private final FieldSet<FieldDescriptor> fields;
|
||||
private final UnknownFieldSet unknownFields;
|
||||
private int memoizedSize = -1;
|
||||
|
||||
/**
|
||||
* Construct a {@code DynamicMessage} using the given {@code FieldSet}.
|
||||
*/
|
||||
private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
|
||||
UnknownFieldSet unknownFields) {
|
||||
this.type = type;
|
||||
this.fields = fields;
|
||||
this.unknownFields = unknownFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@code DynamicMessage} representing the default instance of the
|
||||
* given type.
|
||||
*/
|
||||
public static DynamicMessage getDefaultInstance(Descriptor type) {
|
||||
return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
|
||||
UnknownFieldSet.getDefaultInstance());
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from the given input stream. */
|
||||
public static DynamicMessage parseFrom(Descriptor type,
|
||||
CodedInputStream input)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from the given input stream. */
|
||||
public static DynamicMessage parseFrom(
|
||||
Descriptor type,
|
||||
CodedInputStream input,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, ByteString data,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as a message of the given type and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, byte[] data,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from {@code input} and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, InputStream input)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input).buildParsed();
|
||||
}
|
||||
|
||||
/** Parse a message of the given type from {@code input} and return it. */
|
||||
public static DynamicMessage parseFrom(Descriptor type, InputStream input,
|
||||
ExtensionRegistry extensionRegistry)
|
||||
throws IOException {
|
||||
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
|
||||
}
|
||||
|
||||
/** Construct a {@link Message.Builder} for the given type. */
|
||||
public static Builder newBuilder(Descriptor type) {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link Message.Builder} for a message of the same type as
|
||||
* {@code prototype}, and initialize it with {@code prototype}'s contents.
|
||||
*/
|
||||
public static Builder newBuilder(Message prototype) {
|
||||
return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Implementation of Message interface.
|
||||
|
||||
public Descriptor getDescriptorForType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DynamicMessage getDefaultInstanceForType() {
|
||||
return getDefaultInstance(type);
|
||||
}
|
||||
|
||||
public Map<FieldDescriptor, Object> getAllFields() {
|
||||
return fields.getAllFields();
|
||||
}
|
||||
|
||||
public boolean hasField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.hasField(field);
|
||||
}
|
||||
|
||||
public Object getField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
Object result = fields.getField(field);
|
||||
if (result == null) {
|
||||
if (field.isRepeated()) {
|
||||
result = Collections.emptyList();
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
result = getDefaultInstance(field.getMessageType());
|
||||
} else {
|
||||
result = field.getDefaultValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getRepeatedFieldCount(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedFieldCount(field);
|
||||
}
|
||||
|
||||
public Object getRepeatedField(FieldDescriptor field, int index) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedField(field, index);
|
||||
}
|
||||
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return unknownFields;
|
||||
}
|
||||
|
||||
private static boolean isInitialized(Descriptor type,
|
||||
FieldSet<FieldDescriptor> fields) {
|
||||
// Check that all required fields are present.
|
||||
for (final FieldDescriptor field : type.getFields()) {
|
||||
if (field.isRequired()) {
|
||||
if (!fields.hasField(field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that embedded messages are initialized.
|
||||
return fields.isInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return isInitialized(type, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(CodedOutputStream output) throws IOException {
|
||||
if (type.getOptions().getMessageSetWireFormat()) {
|
||||
fields.writeMessageSetTo(output);
|
||||
unknownFields.writeAsMessageSetTo(output);
|
||||
} else {
|
||||
fields.writeTo(output);
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
if (type.getOptions().getMessageSetWireFormat()) {
|
||||
size = fields.getMessageSetSerializedSize();
|
||||
size += unknownFields.getSerializedSizeAsMessageSet();
|
||||
} else {
|
||||
size = fields.getSerializedSize();
|
||||
size += unknownFields.getSerializedSize();
|
||||
}
|
||||
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
public Builder newBuilderForType() {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return newBuilderForType().mergeFrom(this);
|
||||
}
|
||||
|
||||
public Parser<DynamicMessage> getParserForType() {
|
||||
return new AbstractParser<DynamicMessage>() {
|
||||
public DynamicMessage parsePartialFrom(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
Builder builder = newBuilder(type);
|
||||
try {
|
||||
builder.mergeFrom(input, extensionRegistry);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage())
|
||||
.setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Verifies that the field is a field of this message. */
|
||||
private void verifyContainingType(FieldDescriptor field) {
|
||||
if (field.getContainingType() != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDescriptor does not match message type.");
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* Builder for {@link DynamicMessage}s.
|
||||
*/
|
||||
public static final class Builder extends AbstractMessage.Builder<Builder> {
|
||||
private final Descriptor type;
|
||||
private FieldSet<FieldDescriptor> fields;
|
||||
private UnknownFieldSet unknownFields;
|
||||
|
||||
/** Construct a {@code Builder} for the given type. */
|
||||
private Builder(Descriptor type) {
|
||||
this.type = type;
|
||||
this.fields = FieldSet.newFieldSet();
|
||||
this.unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Implementation of Message.Builder interface.
|
||||
|
||||
@Override
|
||||
public Builder clear() {
|
||||
if (fields.isImmutable()) {
|
||||
fields = FieldSet.newFieldSet();
|
||||
} else {
|
||||
fields.clear();
|
||||
}
|
||||
unknownFields = UnknownFieldSet.getDefaultInstance();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mergeFrom(Message other) {
|
||||
if (other instanceof DynamicMessage) {
|
||||
// This should be somewhat faster than calling super.mergeFrom().
|
||||
DynamicMessage otherDynamicMessage = (DynamicMessage) other;
|
||||
if (otherDynamicMessage.type != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"mergeFrom(Message) can only merge messages of the same type.");
|
||||
}
|
||||
ensureIsMutable();
|
||||
fields.mergeFrom(otherDynamicMessage.fields);
|
||||
mergeUnknownFields(otherDynamicMessage.unknownFields);
|
||||
return this;
|
||||
} else {
|
||||
return super.mergeFrom(other);
|
||||
}
|
||||
}
|
||||
|
||||
public DynamicMessage build() {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
new DynamicMessage(type, fields, unknownFields));
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for DynamicMessage.parseFrom() methods to call. Throws
|
||||
* {@link InvalidProtocolBufferException} instead of
|
||||
* {@link UninitializedMessageException}.
|
||||
*/
|
||||
private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
|
||||
if (!isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
new DynamicMessage(type, fields, unknownFields))
|
||||
.asInvalidProtocolBufferException();
|
||||
}
|
||||
return buildPartial();
|
||||
}
|
||||
|
||||
public DynamicMessage buildPartial() {
|
||||
fields.makeImmutable();
|
||||
DynamicMessage result =
|
||||
new DynamicMessage(type, fields, unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clone() {
|
||||
Builder result = new Builder(type);
|
||||
result.fields.mergeFrom(fields);
|
||||
result.mergeUnknownFields(unknownFields);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return DynamicMessage.isInitialized(type, fields);
|
||||
}
|
||||
|
||||
public Descriptor getDescriptorForType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DynamicMessage getDefaultInstanceForType() {
|
||||
return getDefaultInstance(type);
|
||||
}
|
||||
|
||||
public Map<FieldDescriptor, Object> getAllFields() {
|
||||
return fields.getAllFields();
|
||||
}
|
||||
|
||||
public Builder newBuilderForField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
|
||||
if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"newBuilderForField is only valid for fields with message type.");
|
||||
}
|
||||
|
||||
return new Builder(field.getMessageType());
|
||||
}
|
||||
|
||||
public boolean hasField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.hasField(field);
|
||||
}
|
||||
|
||||
public Object getField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
Object result = fields.getField(field);
|
||||
if (result == null) {
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
result = getDefaultInstance(field.getMessageType());
|
||||
} else {
|
||||
result = field.getDefaultValue();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder setField(FieldDescriptor field, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.setField(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clearField(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.clearField(field);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRepeatedFieldCount(FieldDescriptor field) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedFieldCount(field);
|
||||
}
|
||||
|
||||
public Object getRepeatedField(FieldDescriptor field, int index) {
|
||||
verifyContainingType(field);
|
||||
return fields.getRepeatedField(field, index);
|
||||
}
|
||||
|
||||
public Builder setRepeatedField(FieldDescriptor field,
|
||||
int index, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.setRepeatedField(field, index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addRepeatedField(FieldDescriptor field, Object value) {
|
||||
verifyContainingType(field);
|
||||
ensureIsMutable();
|
||||
fields.addRepeatedField(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return unknownFields;
|
||||
}
|
||||
|
||||
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
|
||||
this.unknownFields = unknownFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
|
||||
this.unknownFields =
|
||||
UnknownFieldSet.newBuilder(this.unknownFields)
|
||||
.mergeFrom(unknownFields)
|
||||
.build();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Verifies that the field is a field of this message. */
|
||||
private void verifyContainingType(FieldDescriptor field) {
|
||||
if (field.getContainingType() != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"FieldDescriptor does not match message type.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureIsMutable() {
|
||||
if (fields.isImmutable()) {
|
||||
fields = fields.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) {
|
||||
// TODO(xiangl): need implementation for dynamic message
|
||||
throw new UnsupportedOperationException(
|
||||
"getFieldBuilder() called on a dynamic message type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
266
java/src/main/java/com/google/protobuf/ExtensionRegistry.java
Normal file
266
java/src/main/java/com/google/protobuf/ExtensionRegistry.java
Normal file
@@ -0,0 +1,266 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A table of known extensions, searchable by name or field number. When
|
||||
* parsing a protocol message that might have extensions, you must provide
|
||||
* an {@code ExtensionRegistry} in which you have registered any extensions
|
||||
* that you want to be able to parse. Otherwise, those extensions will just
|
||||
* be treated like unknown fields.
|
||||
*
|
||||
* <p>For example, if you had the {@code .proto} file:
|
||||
*
|
||||
* <pre>
|
||||
* option java_class = "MyProto";
|
||||
*
|
||||
* message Foo {
|
||||
* extensions 1000 to max;
|
||||
* }
|
||||
*
|
||||
* extend Foo {
|
||||
* optional int32 bar;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Then you might write code like:
|
||||
*
|
||||
* <pre>
|
||||
* ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
* registry.add(MyProto.bar);
|
||||
* MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Background:
|
||||
*
|
||||
* <p>You might wonder why this is necessary. Two alternatives might come to
|
||||
* mind. First, you might imagine a system where generated extensions are
|
||||
* automatically registered when their containing classes are loaded. This
|
||||
* is a popular technique, but is bad design; among other things, it creates a
|
||||
* situation where behavior can change depending on what classes happen to be
|
||||
* loaded. It also introduces a security vulnerability, because an
|
||||
* unprivileged class could cause its code to be called unexpectedly from a
|
||||
* privileged class by registering itself as an extension of the right type.
|
||||
*
|
||||
* <p>Another option you might consider is lazy parsing: do not parse an
|
||||
* extension until it is first requested, at which point the caller must
|
||||
* provide a type to use. This introduces a different set of problems. First,
|
||||
* it would require a mutex lock any time an extension was accessed, which
|
||||
* would be slow. Second, corrupt data would not be detected until first
|
||||
* access, at which point it would be much harder to deal with it. Third, it
|
||||
* could violate the expectation that message objects are immutable, since the
|
||||
* type provided could be any arbitrary message class. An unprivileged user
|
||||
* could take advantage of this to inject a mutable object into a message
|
||||
* belonging to privileged code and create mischief.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class ExtensionRegistry extends ExtensionRegistryLite {
|
||||
/** Construct a new, empty instance. */
|
||||
public static ExtensionRegistry newInstance() {
|
||||
return new ExtensionRegistry();
|
||||
}
|
||||
|
||||
/** Get the unmodifiable singleton empty instance. */
|
||||
public static ExtensionRegistry getEmptyRegistry() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the registry. */
|
||||
@Override
|
||||
public ExtensionRegistry getUnmodifiable() {
|
||||
return new ExtensionRegistry(this);
|
||||
}
|
||||
|
||||
/** A (Descriptor, Message) pair, returned by lookup methods. */
|
||||
public static final class ExtensionInfo {
|
||||
/** The extension's descriptor. */
|
||||
public final FieldDescriptor descriptor;
|
||||
|
||||
/**
|
||||
* A default instance of the extension's type, if it has a message type.
|
||||
* Otherwise, {@code null}.
|
||||
*/
|
||||
public final Message defaultInstance;
|
||||
|
||||
private ExtensionInfo(final FieldDescriptor descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
defaultInstance = null;
|
||||
}
|
||||
private ExtensionInfo(final FieldDescriptor descriptor,
|
||||
final Message defaultInstance) {
|
||||
this.descriptor = descriptor;
|
||||
this.defaultInstance = defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by fully-qualified field name, in the proto namespace.
|
||||
* I.e. {@code result.descriptor.fullName()} will match {@code fullName} if
|
||||
* a match is found.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public ExtensionInfo findExtensionByName(final String fullName) {
|
||||
return extensionsByName.get(fullName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by containing type and field number.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public ExtensionInfo findExtensionByNumber(final Descriptor containingType,
|
||||
final int fieldNumber) {
|
||||
return extensionsByNumber.get(
|
||||
new DescriptorIntPair(containingType, fieldNumber));
|
||||
}
|
||||
|
||||
/** Add an extension from a generated file to the registry. */
|
||||
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
|
||||
if (extension.getDescriptor().getJavaType() ==
|
||||
FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (extension.getMessageDefaultInstance() == null) {
|
||||
throw new IllegalStateException(
|
||||
"Registered message-type extension had null default instance: " +
|
||||
extension.getDescriptor().getFullName());
|
||||
}
|
||||
add(new ExtensionInfo(extension.getDescriptor(),
|
||||
extension.getMessageDefaultInstance()));
|
||||
} else {
|
||||
add(new ExtensionInfo(extension.getDescriptor(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a non-message-type extension to the registry by descriptor. */
|
||||
public void add(final FieldDescriptor type) {
|
||||
if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() must be provided a default instance when " +
|
||||
"adding an embedded message extension.");
|
||||
}
|
||||
add(new ExtensionInfo(type, null));
|
||||
}
|
||||
|
||||
/** Add a message-type extension to the registry by descriptor. */
|
||||
public void add(final FieldDescriptor type, final Message defaultInstance) {
|
||||
if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() provided a default instance for a " +
|
||||
"non-message extension.");
|
||||
}
|
||||
add(new ExtensionInfo(type, defaultInstance));
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Private stuff.
|
||||
|
||||
private ExtensionRegistry() {
|
||||
this.extensionsByName = new HashMap<String, ExtensionInfo>();
|
||||
this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>();
|
||||
}
|
||||
|
||||
private ExtensionRegistry(ExtensionRegistry other) {
|
||||
super(other);
|
||||
this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName);
|
||||
this.extensionsByNumber =
|
||||
Collections.unmodifiableMap(other.extensionsByNumber);
|
||||
}
|
||||
|
||||
private final Map<String, ExtensionInfo> extensionsByName;
|
||||
private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
|
||||
|
||||
private ExtensionRegistry(boolean empty) {
|
||||
super(ExtensionRegistryLite.getEmptyRegistry());
|
||||
this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap();
|
||||
this.extensionsByNumber =
|
||||
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
|
||||
}
|
||||
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
|
||||
|
||||
private void add(final ExtensionInfo extension) {
|
||||
if (!extension.descriptor.isExtension()) {
|
||||
throw new IllegalArgumentException(
|
||||
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
|
||||
"(non-extension) field.");
|
||||
}
|
||||
|
||||
extensionsByName.put(extension.descriptor.getFullName(), extension);
|
||||
extensionsByNumber.put(
|
||||
new DescriptorIntPair(extension.descriptor.getContainingType(),
|
||||
extension.descriptor.getNumber()),
|
||||
extension);
|
||||
|
||||
final FieldDescriptor field = extension.descriptor;
|
||||
if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
|
||||
field.getType() == FieldDescriptor.Type.MESSAGE &&
|
||||
field.isOptional() &&
|
||||
field.getExtensionScope() == field.getMessageType()) {
|
||||
// This is an extension of a MessageSet type defined within the extension
|
||||
// type's own scope. For backwards-compatibility, allow it to be looked
|
||||
// up by type name.
|
||||
extensionsByName.put(field.getMessageType().getFullName(), extension);
|
||||
}
|
||||
}
|
||||
|
||||
/** A (GenericDescriptor, int) pair, used as a map key. */
|
||||
private static final class DescriptorIntPair {
|
||||
private final Descriptor descriptor;
|
||||
private final int number;
|
||||
|
||||
DescriptorIntPair(final Descriptor descriptor, final int number) {
|
||||
this.descriptor = descriptor;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return descriptor.hashCode() * ((1 << 16) - 1) + number;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof DescriptorIntPair)) {
|
||||
return false;
|
||||
}
|
||||
final DescriptorIntPair other = (DescriptorIntPair)obj;
|
||||
return descriptor == other.descriptor && number == other.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Equivalent to {@link ExtensionRegistry} but supports only "lite" types.
|
||||
* <p>
|
||||
* If all of your types are lite types, then you only need to use
|
||||
* {@code ExtensionRegistryLite}. Similarly, if all your types are regular
|
||||
* types, then you only need {@link ExtensionRegistry}. Typically it does not
|
||||
* make sense to mix the two, since if you have any regular types in your
|
||||
* program, you then require the full runtime and lose all the benefits of
|
||||
* the lite runtime, so you might as well make all your types be regular types.
|
||||
* However, in some cases (e.g. when depending on multiple third-party libraries
|
||||
* where one uses lite types and one uses regular), you may find yourself
|
||||
* wanting to mix the two. In this case things get more complicated.
|
||||
* <p>
|
||||
* There are three factors to consider: Whether the type being extended is
|
||||
* lite, whether the embedded type (in the case of a message-typed extension)
|
||||
* is lite, and whether the extension itself is lite. Since all three are
|
||||
* declared in different files, they could all be different. Here are all
|
||||
* the combinations and which type of registry to use:
|
||||
* <pre>
|
||||
* Extended type Inner type Extension Use registry
|
||||
* =======================================================================
|
||||
* lite lite lite ExtensionRegistryLite
|
||||
* lite regular lite ExtensionRegistry
|
||||
* regular regular regular ExtensionRegistry
|
||||
* all other combinations not supported
|
||||
* </pre>
|
||||
* <p>
|
||||
* Note that just as regular types are not allowed to contain lite-type fields,
|
||||
* they are also not allowed to contain lite-type extensions. This is because
|
||||
* regular types must be fully accessible via reflection, which in turn means
|
||||
* that all the inner messages must also support reflection. On the other hand,
|
||||
* since regular types implement the entire lite interface, there is no problem
|
||||
* with embedding regular types inside lite types.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class ExtensionRegistryLite {
|
||||
|
||||
// Set true to enable lazy parsing feature for MessageSet.
|
||||
//
|
||||
// TODO(xiangl): Now we use a global flag to control whether enable lazy
|
||||
// parsing feature for MessageSet, which may be too crude for some
|
||||
// applications. Need to support this feature on smaller granularity.
|
||||
private static volatile boolean eagerlyParseMessageSets = false;
|
||||
|
||||
public static boolean isEagerlyParseMessageSets() {
|
||||
return eagerlyParseMessageSets;
|
||||
}
|
||||
|
||||
public static void setEagerlyParseMessageSets(boolean isEagerlyParse) {
|
||||
eagerlyParseMessageSets = isEagerlyParse;
|
||||
}
|
||||
|
||||
/** Construct a new, empty instance. */
|
||||
public static ExtensionRegistryLite newInstance() {
|
||||
return new ExtensionRegistryLite();
|
||||
}
|
||||
|
||||
/** Get the unmodifiable singleton empty instance. */
|
||||
public static ExtensionRegistryLite getEmptyRegistry() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the registry. */
|
||||
public ExtensionRegistryLite getUnmodifiable() {
|
||||
return new ExtensionRegistryLite(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an extension by containing type and field number.
|
||||
*
|
||||
* @return Information about the extension if found, or {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <ContainingType extends MessageLite>
|
||||
GeneratedMessageLite.GeneratedExtension<ContainingType, ?>
|
||||
findLiteExtensionByNumber(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final int fieldNumber) {
|
||||
return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>)
|
||||
extensionsByNumber.get(
|
||||
new ObjectIntPair(containingTypeDefaultInstance, fieldNumber));
|
||||
}
|
||||
|
||||
/** Add an extension from a lite generated file to the registry. */
|
||||
public final void add(
|
||||
final GeneratedMessageLite.GeneratedExtension<?, ?> extension) {
|
||||
extensionsByNumber.put(
|
||||
new ObjectIntPair(extension.getContainingTypeDefaultInstance(),
|
||||
extension.getNumber()),
|
||||
extension);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Private stuff.
|
||||
|
||||
// Constructors are package-private so that ExtensionRegistry can subclass
|
||||
// this.
|
||||
|
||||
ExtensionRegistryLite() {
|
||||
this.extensionsByNumber =
|
||||
new HashMap<ObjectIntPair,
|
||||
GeneratedMessageLite.GeneratedExtension<?, ?>>();
|
||||
}
|
||||
|
||||
ExtensionRegistryLite(ExtensionRegistryLite other) {
|
||||
if (other == EMPTY) {
|
||||
this.extensionsByNumber = Collections.emptyMap();
|
||||
} else {
|
||||
this.extensionsByNumber =
|
||||
Collections.unmodifiableMap(other.extensionsByNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<ObjectIntPair,
|
||||
GeneratedMessageLite.GeneratedExtension<?, ?>>
|
||||
extensionsByNumber;
|
||||
|
||||
private ExtensionRegistryLite(boolean empty) {
|
||||
this.extensionsByNumber = Collections.emptyMap();
|
||||
}
|
||||
private static final ExtensionRegistryLite EMPTY =
|
||||
new ExtensionRegistryLite(true);
|
||||
|
||||
/** A (Object, int) pair, used as a map key. */
|
||||
private static final class ObjectIntPair {
|
||||
private final Object object;
|
||||
private final int number;
|
||||
|
||||
ObjectIntPair(final Object object, final int number) {
|
||||
this.object = object;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(object) * ((1 << 16) - 1) + number;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof ObjectIntPair)) {
|
||||
return false;
|
||||
}
|
||||
final ObjectIntPair other = (ObjectIntPair)obj;
|
||||
return object == other.object && number == other.number;
|
||||
}
|
||||
}
|
||||
}
|
||||
861
java/src/main/java/com/google/protobuf/FieldSet.java
Normal file
861
java/src/main/java/com/google/protobuf/FieldSet.java
Normal file
@@ -0,0 +1,861 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.LazyField.LazyIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class which represents an arbitrary set of fields of some message type.
|
||||
* This is used to implement {@link DynamicMessage}, and also to represent
|
||||
* extensions in {@link GeneratedMessage}. This class is package-private,
|
||||
* since outside users should probably be using {@link DynamicMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
final class FieldSet<FieldDescriptorType extends
|
||||
FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
|
||||
/**
|
||||
* Interface for a FieldDescriptor or lite extension descriptor. This
|
||||
* prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
|
||||
*/
|
||||
public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
|
||||
extends Comparable<T> {
|
||||
int getNumber();
|
||||
WireFormat.FieldType getLiteType();
|
||||
WireFormat.JavaType getLiteJavaType();
|
||||
boolean isRepeated();
|
||||
boolean isPacked();
|
||||
Internal.EnumLiteMap<?> getEnumType();
|
||||
|
||||
// If getLiteJavaType() == MESSAGE, this merges a message object of the
|
||||
// type into a builder of the type. Returns {@code to}.
|
||||
MessageLite.Builder internalMergeFrom(
|
||||
MessageLite.Builder to, MessageLite from);
|
||||
}
|
||||
|
||||
private final SmallSortedMap<FieldDescriptorType, Object> fields;
|
||||
private boolean isImmutable;
|
||||
private boolean hasLazyField = false;
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
private FieldSet() {
|
||||
this.fields = SmallSortedMap.newFieldMap(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an empty FieldSet. This is only used to initialize
|
||||
* DEFAULT_INSTANCE.
|
||||
*/
|
||||
private FieldSet(final boolean dummy) {
|
||||
this.fields = SmallSortedMap.newFieldMap(0);
|
||||
makeImmutable();
|
||||
}
|
||||
|
||||
/** Construct a new FieldSet. */
|
||||
public static <T extends FieldSet.FieldDescriptorLite<T>>
|
||||
FieldSet<T> newFieldSet() {
|
||||
return new FieldSet<T>();
|
||||
}
|
||||
|
||||
/** Get an immutable empty FieldSet. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FieldSet.FieldDescriptorLite<T>>
|
||||
FieldSet<T> emptySet() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
|
||||
|
||||
/** Make this FieldSet immutable from this point forward. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
if (isImmutable) {
|
||||
return;
|
||||
}
|
||||
fields.makeImmutable();
|
||||
isImmutable = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the FieldSet is immutable. This is true if it is the
|
||||
* {@link #emptySet} or if {@link #makeImmutable} were called.
|
||||
*
|
||||
* @return whether the FieldSet is immutable.
|
||||
*/
|
||||
public boolean isImmutable() {
|
||||
return isImmutable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the FieldSet. The returned FieldSet will be mutable even if the
|
||||
* original FieldSet was immutable.
|
||||
*
|
||||
* @return the newly cloned FieldSet
|
||||
*/
|
||||
@Override
|
||||
public FieldSet<FieldDescriptorType> clone() {
|
||||
// We can't just call fields.clone because List objects in the map
|
||||
// should not be shared.
|
||||
FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
|
||||
FieldDescriptorType descriptor = entry.getKey();
|
||||
clone.setField(descriptor, entry.getValue());
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
FieldDescriptorType descriptor = entry.getKey();
|
||||
clone.setField(descriptor, entry.getValue());
|
||||
}
|
||||
clone.hasLazyField = hasLazyField;
|
||||
return clone;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/** See {@link Message.Builder#clear()}. */
|
||||
public void clear() {
|
||||
fields.clear();
|
||||
hasLazyField = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a simple map containing all the fields.
|
||||
*/
|
||||
public Map<FieldDescriptorType, Object> getAllFields() {
|
||||
if (hasLazyField) {
|
||||
SmallSortedMap<FieldDescriptorType, Object> result =
|
||||
SmallSortedMap.newFieldMap(16);
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
cloneFieldEntry(result, fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
cloneFieldEntry(result, entry);
|
||||
}
|
||||
if (fields.isImmutable()) {
|
||||
result.makeImmutable();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
private void cloneFieldEntry(Map<FieldDescriptorType, Object> map,
|
||||
Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
FieldDescriptorType key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof LazyField) {
|
||||
map.put(key, ((LazyField) value).getValue());
|
||||
} else {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator to the field map. This iterator should not be leaked out
|
||||
* of the protobuf library as it is not protected from mutation when fields
|
||||
* is not immutable.
|
||||
*/
|
||||
public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
|
||||
if (hasLazyField) {
|
||||
return new LazyIterator<FieldDescriptorType>(
|
||||
fields.entrySet().iterator());
|
||||
}
|
||||
return fields.entrySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#hasField(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public boolean hasField(final FieldDescriptorType descriptor) {
|
||||
if (descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"hasField() can only be called on non-repeated fields.");
|
||||
}
|
||||
|
||||
return fields.get(descriptor) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)}. This method
|
||||
* returns {@code null} if the field is not set; in this case it is up
|
||||
* to the caller to fetch the field's default value.
|
||||
*/
|
||||
public Object getField(final FieldDescriptorType descriptor) {
|
||||
Object o = fields.get(descriptor);
|
||||
if (o instanceof LazyField) {
|
||||
return ((LazyField) o).getValue();
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setField(final FieldDescriptorType descriptor,
|
||||
Object value) {
|
||||
if (descriptor.isRepeated()) {
|
||||
if (!(value instanceof List)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
|
||||
// Wrap the contents in a new list so that the caller cannot change
|
||||
// the list's contents after setting it.
|
||||
final List newList = new ArrayList();
|
||||
newList.addAll((List) value);
|
||||
for (final Object element : newList) {
|
||||
verifyType(descriptor.getLiteType(), element);
|
||||
}
|
||||
value = newList;
|
||||
} else {
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
}
|
||||
|
||||
if (value instanceof LazyField) {
|
||||
hasLazyField = true;
|
||||
}
|
||||
fields.put(descriptor, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public void clearField(final FieldDescriptorType descriptor) {
|
||||
fields.remove(descriptor);
|
||||
if (fields.isEmpty()) {
|
||||
hasLazyField = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
|
||||
*/
|
||||
public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return ((List<?>) value).size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
|
||||
*/
|
||||
public Object getRepeatedField(final FieldDescriptorType descriptor,
|
||||
final int index) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object value = getField(descriptor);
|
||||
|
||||
if (value == null) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else {
|
||||
return ((List<?>) value).get(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setRepeatedField(final FieldDescriptorType descriptor,
|
||||
final int index,
|
||||
final Object value) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"getRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
final Object list = getField(descriptor);
|
||||
if (list == null) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
((List<Object>) list).set(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for implementing
|
||||
* {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addRepeatedField(final FieldDescriptorType descriptor,
|
||||
final Object value) {
|
||||
if (!descriptor.isRepeated()) {
|
||||
throw new IllegalArgumentException(
|
||||
"addRepeatedField() can only be called on repeated fields.");
|
||||
}
|
||||
|
||||
verifyType(descriptor.getLiteType(), value);
|
||||
|
||||
final Object existingValue = getField(descriptor);
|
||||
List<Object> list;
|
||||
if (existingValue == null) {
|
||||
list = new ArrayList<Object>();
|
||||
fields.put(descriptor, list);
|
||||
} else {
|
||||
list = (List<Object>) existingValue;
|
||||
}
|
||||
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given object is of the correct type to be a valid
|
||||
* value for the given field. (For repeated fields, this checks if the
|
||||
* object is the right type to be one element of the field.)
|
||||
*
|
||||
* @throws IllegalArgumentException The value is not of the right type.
|
||||
*/
|
||||
private static void verifyType(final WireFormat.FieldType type,
|
||||
final Object value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
boolean isValid = false;
|
||||
switch (type.getJavaType()) {
|
||||
case INT: isValid = value instanceof Integer ; break;
|
||||
case LONG: isValid = value instanceof Long ; break;
|
||||
case FLOAT: isValid = value instanceof Float ; break;
|
||||
case DOUBLE: isValid = value instanceof Double ; break;
|
||||
case BOOLEAN: isValid = value instanceof Boolean ; break;
|
||||
case STRING: isValid = value instanceof String ; break;
|
||||
case BYTE_STRING: isValid = value instanceof ByteString; break;
|
||||
case ENUM:
|
||||
// TODO(kenton): Caller must do type checking here, I guess.
|
||||
isValid = value instanceof Internal.EnumLite;
|
||||
break;
|
||||
case MESSAGE:
|
||||
// TODO(kenton): Caller must do type checking here, I guess.
|
||||
isValid =
|
||||
(value instanceof MessageLite) || (value instanceof LazyField);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
// TODO(kenton): When chaining calls to setField(), it can be hard to
|
||||
// tell from the stack trace which exact call failed, since the whole
|
||||
// chain is considered one line of code. It would be nice to print
|
||||
// more information here, e.g. naming the field. We used to do that.
|
||||
// But we can't now that FieldSet doesn't use descriptors. Maybe this
|
||||
// isn't a big deal, though, since it would only really apply when using
|
||||
// reflection and generally people don't chain reflection setters.
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Parsing and serialization
|
||||
|
||||
/**
|
||||
* See {@link Message#isInitialized()}. Note: Since {@code FieldSet}
|
||||
* itself does not have any way of knowing about required fields that
|
||||
* aren't actually present in the set, it is up to the caller to check
|
||||
* that all required fields are present.
|
||||
*/
|
||||
public boolean isInitialized() {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
if (!isInitialized(fields.getArrayEntryAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
if (!isInitialized(entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isInitialized(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
if (descriptor.isRepeated()) {
|
||||
for (final MessageLite element:
|
||||
(List<MessageLite>) entry.getValue()) {
|
||||
if (!element.isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof MessageLite) {
|
||||
if (!((MessageLite) value).isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
} else if (value instanceof LazyField) {
|
||||
return true;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong object type used with protocol message reflection.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a field type, return the wire type.
|
||||
*
|
||||
* @returns One of the {@code WIRETYPE_} constants defined in
|
||||
* {@link WireFormat}.
|
||||
*/
|
||||
static int getWireFormatForFieldType(final WireFormat.FieldType type,
|
||||
boolean isPacked) {
|
||||
if (isPacked) {
|
||||
return WireFormat.WIRETYPE_LENGTH_DELIMITED;
|
||||
} else {
|
||||
return type.getWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
|
||||
* {@link FieldSet}.
|
||||
*/
|
||||
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
|
||||
for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
|
||||
mergeFromField(other.fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
other.fields.getOverflowEntries()) {
|
||||
mergeFromField(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void mergeFromField(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
Object otherValue = entry.getValue();
|
||||
if (otherValue instanceof LazyField) {
|
||||
otherValue = ((LazyField) otherValue).getValue();
|
||||
}
|
||||
|
||||
if (descriptor.isRepeated()) {
|
||||
Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
// Our list is empty, but we still need to make a defensive copy of
|
||||
// the other list since we don't know if the other FieldSet is still
|
||||
// mutable.
|
||||
fields.put(descriptor, new ArrayList((List) otherValue));
|
||||
} else {
|
||||
// Concatenate the lists.
|
||||
((List) value).addAll((List) otherValue);
|
||||
}
|
||||
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
|
||||
Object value = getField(descriptor);
|
||||
if (value == null) {
|
||||
fields.put(descriptor, otherValue);
|
||||
} else {
|
||||
// Merge the messages.
|
||||
fields.put(
|
||||
descriptor,
|
||||
descriptor.internalMergeFrom(
|
||||
((MessageLite) value).toBuilder(), (MessageLite) otherValue)
|
||||
.build());
|
||||
}
|
||||
} else {
|
||||
fields.put(descriptor, otherValue);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kenton): Move static parsing and serialization methods into some
|
||||
// other class. Probably WireFormat.
|
||||
|
||||
/**
|
||||
* Read a field of any primitive type from a CodedInputStream. Enums,
|
||||
* groups, and embedded messages are not handled by this method.
|
||||
*
|
||||
* @param input The stream from which to read.
|
||||
* @param type Declared type of the field.
|
||||
* @return An object representing the field's value, of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
public static Object readPrimitiveField(
|
||||
CodedInputStream input,
|
||||
final WireFormat.FieldType type) throws IOException {
|
||||
switch (type) {
|
||||
case DOUBLE : return input.readDouble ();
|
||||
case FLOAT : return input.readFloat ();
|
||||
case INT64 : return input.readInt64 ();
|
||||
case UINT64 : return input.readUInt64 ();
|
||||
case INT32 : return input.readInt32 ();
|
||||
case FIXED64 : return input.readFixed64 ();
|
||||
case FIXED32 : return input.readFixed32 ();
|
||||
case BOOL : return input.readBool ();
|
||||
case STRING : return input.readString ();
|
||||
case BYTES : return input.readBytes ();
|
||||
case UINT32 : return input.readUInt32 ();
|
||||
case SFIXED32: return input.readSFixed32();
|
||||
case SFIXED64: return input.readSFixed64();
|
||||
case SINT32 : return input.readSInt32 ();
|
||||
case SINT64 : return input.readSInt64 ();
|
||||
|
||||
case GROUP:
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle nested groups.");
|
||||
case MESSAGE:
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle embedded messages.");
|
||||
case ENUM:
|
||||
// We don't handle enums because we don't know what to do if the
|
||||
// value is not recognized.
|
||||
throw new IllegalArgumentException(
|
||||
"readPrimitiveField() cannot handle enums.");
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"There is no way to get here, but the compiler thinks otherwise.");
|
||||
}
|
||||
|
||||
/** See {@link Message#writeTo(CodedOutputStream)}. */
|
||||
public void writeTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
fields.getArrayEntryAt(i);
|
||||
writeField(entry.getKey(), entry.getValue(), output);
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
writeField(entry.getKey(), entry.getValue(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #writeTo} but uses MessageSet wire format.
|
||||
*/
|
||||
public void writeMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
writeMessageSetTo(fields.getArrayEntryAt(i), output);
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
writeMessageSetTo(entry, output);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMessageSetTo(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry,
|
||||
final CodedOutputStream output) throws IOException {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
|
||||
!descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
output.writeMessageSetExtension(entry.getKey().getNumber(),
|
||||
(MessageLite) entry.getValue());
|
||||
} else {
|
||||
writeField(descriptor, entry.getValue(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single tag-value pair to the stream.
|
||||
*
|
||||
* @param output The output stream.
|
||||
* @param type The field's type.
|
||||
* @param number The field's number.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static void writeElement(final CodedOutputStream output,
|
||||
final WireFormat.FieldType type,
|
||||
final int number,
|
||||
final Object value) throws IOException {
|
||||
// Special case for groups, which need a start and end tag; other fields
|
||||
// can just use writeTag() and writeFieldNoTag().
|
||||
if (type == WireFormat.FieldType.GROUP) {
|
||||
output.writeGroup(number, (MessageLite) value);
|
||||
} else {
|
||||
output.writeTag(number, getWireFormatForFieldType(type, false));
|
||||
writeElementNoTag(output, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a field of arbitrary type, without its tag, to the stream.
|
||||
*
|
||||
* @param output The output stream.
|
||||
* @param type The field's type.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static void writeElementNoTag(
|
||||
final CodedOutputStream output,
|
||||
final WireFormat.FieldType type,
|
||||
final Object value) throws IOException {
|
||||
switch (type) {
|
||||
case DOUBLE : output.writeDoubleNoTag ((Double ) value); break;
|
||||
case FLOAT : output.writeFloatNoTag ((Float ) value); break;
|
||||
case INT64 : output.writeInt64NoTag ((Long ) value); break;
|
||||
case UINT64 : output.writeUInt64NoTag ((Long ) value); break;
|
||||
case INT32 : output.writeInt32NoTag ((Integer ) value); break;
|
||||
case FIXED64 : output.writeFixed64NoTag ((Long ) value); break;
|
||||
case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break;
|
||||
case BOOL : output.writeBoolNoTag ((Boolean ) value); break;
|
||||
case STRING : output.writeStringNoTag ((String ) value); break;
|
||||
case GROUP : output.writeGroupNoTag ((MessageLite) value); break;
|
||||
case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
|
||||
case BYTES : output.writeBytesNoTag ((ByteString ) value); break;
|
||||
case UINT32 : output.writeUInt32NoTag ((Integer ) value); break;
|
||||
case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break;
|
||||
case SFIXED64: output.writeSFixed64NoTag((Long ) value); break;
|
||||
case SINT32 : output.writeSInt32NoTag ((Integer ) value); break;
|
||||
case SINT64 : output.writeSInt64NoTag ((Long ) value); break;
|
||||
|
||||
case ENUM:
|
||||
output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Write a single field. */
|
||||
public static void writeField(final FieldDescriptorLite<?> descriptor,
|
||||
final Object value,
|
||||
final CodedOutputStream output)
|
||||
throws IOException {
|
||||
WireFormat.FieldType type = descriptor.getLiteType();
|
||||
int number = descriptor.getNumber();
|
||||
if (descriptor.isRepeated()) {
|
||||
final List<?> valueList = (List<?>)value;
|
||||
if (descriptor.isPacked()) {
|
||||
output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
// Compute the total data size so the length can be written.
|
||||
int dataSize = 0;
|
||||
for (final Object element : valueList) {
|
||||
dataSize += computeElementSizeNoTag(type, element);
|
||||
}
|
||||
output.writeRawVarint32(dataSize);
|
||||
// Write the data itself, without any tags.
|
||||
for (final Object element : valueList) {
|
||||
writeElementNoTag(output, type, element);
|
||||
}
|
||||
} else {
|
||||
for (final Object element : valueList) {
|
||||
writeElement(output, type, number, element);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value instanceof LazyField) {
|
||||
writeElement(output, type, number, ((LazyField) value).getValue());
|
||||
} else {
|
||||
writeElement(output, type, number, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Message#getSerializedSize()}. It's up to the caller to cache
|
||||
* the resulting size if desired.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
int size = 0;
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
fields.getArrayEntryAt(i);
|
||||
size += computeFieldSize(entry.getKey(), entry.getValue());
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
size += computeFieldSize(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getSerializedSize} but uses MessageSet wire format.
|
||||
*/
|
||||
public int getMessageSetSerializedSize() {
|
||||
int size = 0;
|
||||
for (int i = 0; i < fields.getNumArrayEntries(); i++) {
|
||||
size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
|
||||
}
|
||||
for (final Map.Entry<FieldDescriptorType, Object> entry :
|
||||
fields.getOverflowEntries()) {
|
||||
size += getMessageSetSerializedSize(entry);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private int getMessageSetSerializedSize(
|
||||
final Map.Entry<FieldDescriptorType, Object> entry) {
|
||||
final FieldDescriptorType descriptor = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE
|
||||
&& !descriptor.isRepeated() && !descriptor.isPacked()) {
|
||||
if (value instanceof LazyField) {
|
||||
return CodedOutputStream.computeLazyFieldMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (LazyField) value);
|
||||
} else {
|
||||
return CodedOutputStream.computeMessageSetExtensionSize(
|
||||
entry.getKey().getNumber(), (MessageLite) value);
|
||||
}
|
||||
} else {
|
||||
return computeFieldSize(descriptor, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* single tag/value pair of arbitrary type.
|
||||
*
|
||||
* @param type The field's type.
|
||||
* @param number The field's number.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static int computeElementSize(
|
||||
final WireFormat.FieldType type,
|
||||
final int number, final Object value) {
|
||||
int tagSize = CodedOutputStream.computeTagSize(number);
|
||||
if (type == WireFormat.FieldType.GROUP) {
|
||||
tagSize *= 2;
|
||||
}
|
||||
return tagSize + computeElementSizeNoTag(type, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes that would be needed to encode a
|
||||
* particular value of arbitrary type, excluding tag.
|
||||
*
|
||||
* @param type The field's type.
|
||||
* @param value Object representing the field's value. Must be of the exact
|
||||
* type which would be returned by
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} for
|
||||
* this field.
|
||||
*/
|
||||
private static int computeElementSizeNoTag(
|
||||
final WireFormat.FieldType type, final Object value) {
|
||||
switch (type) {
|
||||
// Note: Minor violation of 80-char limit rule here because this would
|
||||
// actually be harder to read if we wrapped the lines.
|
||||
case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value);
|
||||
case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value);
|
||||
case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value);
|
||||
case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value);
|
||||
case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value);
|
||||
case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value);
|
||||
case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value);
|
||||
case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value);
|
||||
case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value);
|
||||
case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value);
|
||||
case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value);
|
||||
case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value);
|
||||
case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value);
|
||||
case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value);
|
||||
case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value);
|
||||
case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value);
|
||||
|
||||
case MESSAGE:
|
||||
if (value instanceof LazyField) {
|
||||
return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value);
|
||||
} else {
|
||||
return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value);
|
||||
}
|
||||
|
||||
case ENUM:
|
||||
return CodedOutputStream.computeEnumSizeNoTag(
|
||||
((Internal.EnumLite) value).getNumber());
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"There is no way to get here, but the compiler thinks otherwise.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the number of bytes needed to encode a particular field.
|
||||
*/
|
||||
public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
|
||||
final Object value) {
|
||||
WireFormat.FieldType type = descriptor.getLiteType();
|
||||
int number = descriptor.getNumber();
|
||||
if (descriptor.isRepeated()) {
|
||||
if (descriptor.isPacked()) {
|
||||
int dataSize = 0;
|
||||
for (final Object element : (List<?>)value) {
|
||||
dataSize += computeElementSizeNoTag(type, element);
|
||||
}
|
||||
return dataSize +
|
||||
CodedOutputStream.computeTagSize(number) +
|
||||
CodedOutputStream.computeRawVarint32Size(dataSize);
|
||||
} else {
|
||||
int size = 0;
|
||||
for (final Object element : (List<?>)value) {
|
||||
size += computeElementSize(type, number, element);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
} else {
|
||||
return computeElementSize(type, number, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
1939
java/src/main/java/com/google/protobuf/GeneratedMessage.java
Normal file
1939
java/src/main/java/com/google/protobuf/GeneratedMessage.java
Normal file
File diff suppressed because it is too large
Load Diff
797
java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
Normal file
797
java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
Normal file
@@ -0,0 +1,797 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lite version of {@link GeneratedMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public abstract class GeneratedMessageLite extends AbstractMessageLite
|
||||
implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected GeneratedMessageLite() {
|
||||
}
|
||||
|
||||
protected GeneratedMessageLite(Builder builder) {
|
||||
}
|
||||
|
||||
public Parser<? extends MessageLite> getParserForType() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
return input.skipField(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by parsing constructors in generated classes.
|
||||
*/
|
||||
protected void makeExtensionsImmutable() {
|
||||
// Noop for messages without extensions.
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class Builder<MessageType extends GeneratedMessageLite,
|
||||
BuilderType extends Builder>
|
||||
extends AbstractMessageLite.Builder<BuilderType> {
|
||||
protected Builder() {}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public BuilderType clear() {
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
// This is implemented here only to work around an apparent bug in the
|
||||
// Java compiler and/or build system. See bug #1898463. The mere presence
|
||||
// of this dummy clone() implementation makes it go away.
|
||||
@Override
|
||||
public BuilderType clone() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/** All subclasses implement this. */
|
||||
public abstract BuilderType mergeFrom(MessageType message);
|
||||
|
||||
// Defined here for return type covariance.
|
||||
public abstract MessageType getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
return input.skipField(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Extensions-related stuff
|
||||
|
||||
/**
|
||||
* Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}.
|
||||
*/
|
||||
public interface ExtendableMessageOrBuilder<
|
||||
MessageType extends ExtendableMessage> extends MessageLiteOrBuilder {
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
<Type> boolean hasExtension(
|
||||
GeneratedExtension<MessageType, Type> extension);
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
<Type> int getExtensionCount(
|
||||
GeneratedExtension<MessageType, List<Type>> extension);
|
||||
|
||||
/** Get the value of an extension. */
|
||||
<Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
<Type> Type getExtension(
|
||||
GeneratedExtension<MessageType, List<Type>> extension,
|
||||
int index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent of {@link GeneratedMessage.ExtendableMessage}.
|
||||
*/
|
||||
public abstract static class ExtendableMessage<
|
||||
MessageType extends ExtendableMessage<MessageType>>
|
||||
extends GeneratedMessageLite
|
||||
implements ExtendableMessageOrBuilder<MessageType> {
|
||||
|
||||
private final FieldSet<ExtensionDescriptor> extensions;
|
||||
|
||||
protected ExtendableMessage() {
|
||||
this.extensions = FieldSet.newFieldSet();
|
||||
}
|
||||
|
||||
protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) {
|
||||
this.extensions = builder.buildExtensions();
|
||||
}
|
||||
|
||||
private void verifyExtensionContainingType(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
if (extension.getContainingTypeDefaultInstance() !=
|
||||
getDefaultInstanceForType()) {
|
||||
// This can only happen if someone uses unchecked operations.
|
||||
throw new IllegalArgumentException(
|
||||
"This extension is for a different message type. Please make " +
|
||||
"sure that you are not suppressing any generics type warnings.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.hasField(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.getRepeatedFieldCount(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
final Object value = extensions.getField(extension.descriptor);
|
||||
if (value == null) {
|
||||
return extension.defaultValue;
|
||||
} else {
|
||||
return (Type) value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return (Type) extensions.getRepeatedField(extension.descriptor, index);
|
||||
}
|
||||
|
||||
/** Called by subclasses to check if all extensions are initialized. */
|
||||
protected boolean extensionsAreInitialized() {
|
||||
return extensions.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field or an extension.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
@Override
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
return GeneratedMessageLite.parseUnknownField(
|
||||
extensions,
|
||||
getDefaultInstanceForType(),
|
||||
input,
|
||||
extensionRegistry,
|
||||
tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by parsing constructors in generated classes.
|
||||
*/
|
||||
@Override
|
||||
protected void makeExtensionsImmutable() {
|
||||
extensions.makeImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by subclasses to serialize extensions. Extension ranges may be
|
||||
* interleaved with field numbers, but we must write them in canonical
|
||||
* (sorted by field number) order. ExtensionWriter helps us write
|
||||
* individual ranges of extensions at once.
|
||||
*/
|
||||
protected class ExtensionWriter {
|
||||
// Imagine how much simpler this code would be if Java iterators had
|
||||
// a way to get the next element without advancing the iterator.
|
||||
|
||||
private final Iterator<Map.Entry<ExtensionDescriptor, Object>> iter =
|
||||
extensions.iterator();
|
||||
private Map.Entry<ExtensionDescriptor, Object> next;
|
||||
private final boolean messageSetWireFormat;
|
||||
|
||||
private ExtensionWriter(boolean messageSetWireFormat) {
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
}
|
||||
this.messageSetWireFormat = messageSetWireFormat;
|
||||
}
|
||||
|
||||
public void writeUntil(final int end, final CodedOutputStream output)
|
||||
throws IOException {
|
||||
while (next != null && next.getKey().getNumber() < end) {
|
||||
ExtensionDescriptor extension = next.getKey();
|
||||
if (messageSetWireFormat && extension.getLiteJavaType() ==
|
||||
WireFormat.JavaType.MESSAGE &&
|
||||
!extension.isRepeated()) {
|
||||
output.writeMessageSetExtension(extension.getNumber(),
|
||||
(MessageLite) next.getValue());
|
||||
} else {
|
||||
FieldSet.writeField(extension, next.getValue(), output);
|
||||
}
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ExtensionWriter newExtensionWriter() {
|
||||
return new ExtensionWriter(false);
|
||||
}
|
||||
protected ExtensionWriter newMessageSetExtensionWriter() {
|
||||
return new ExtensionWriter(true);
|
||||
}
|
||||
|
||||
/** Called by subclasses to compute the size of extensions. */
|
||||
protected int extensionsSerializedSize() {
|
||||
return extensions.getSerializedSize();
|
||||
}
|
||||
protected int extensionsSerializedSizeAsMessageSet() {
|
||||
return extensions.getMessageSetSerializedSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent of {@link GeneratedMessage.ExtendableBuilder}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract static class ExtendableBuilder<
|
||||
MessageType extends ExtendableMessage<MessageType>,
|
||||
BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
|
||||
extends Builder<MessageType, BuilderType>
|
||||
implements ExtendableMessageOrBuilder<MessageType> {
|
||||
protected ExtendableBuilder() {}
|
||||
|
||||
private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
|
||||
private boolean extensionsIsMutable;
|
||||
|
||||
@Override
|
||||
public BuilderType clear() {
|
||||
extensions.clear();
|
||||
extensionsIsMutable = false;
|
||||
return super.clear();
|
||||
}
|
||||
|
||||
private void ensureExtensionsIsMutable() {
|
||||
if (!extensionsIsMutable) {
|
||||
extensions = extensions.clone();
|
||||
extensionsIsMutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the build code path to create a copy of the extensions for
|
||||
* building the message.
|
||||
*/
|
||||
private FieldSet<ExtensionDescriptor> buildExtensions() {
|
||||
extensions.makeImmutable();
|
||||
extensionsIsMutable = false;
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private void verifyExtensionContainingType(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
if (extension.getContainingTypeDefaultInstance() !=
|
||||
getDefaultInstanceForType()) {
|
||||
// This can only happen if someone uses unchecked operations.
|
||||
throw new IllegalArgumentException(
|
||||
"This extension is for a different message type. Please make " +
|
||||
"sure that you are not suppressing any generics type warnings.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a singular extension is present. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> boolean hasExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.hasField(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the number of elements in a repeated extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> int getExtensionCount(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return extensions.getRepeatedFieldCount(extension.descriptor);
|
||||
}
|
||||
|
||||
/** Get the value of an extension. */
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
final Object value = extensions.getField(extension.descriptor);
|
||||
if (value == null) {
|
||||
return extension.defaultValue;
|
||||
} else {
|
||||
return (Type) value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get one element of a repeated extension. */
|
||||
@SuppressWarnings("unchecked")
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public final <Type> Type getExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index) {
|
||||
verifyExtensionContainingType(extension);
|
||||
return (Type) extensions.getRepeatedField(extension.descriptor, index);
|
||||
}
|
||||
|
||||
// This is implemented here only to work around an apparent bug in the
|
||||
// Java compiler and/or build system. See bug #1898463. The mere presence
|
||||
// of this dummy clone() implementation makes it go away.
|
||||
@Override
|
||||
public BuilderType clone() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is supposed to be overridden by subclasses.");
|
||||
}
|
||||
|
||||
/** Set the value of an extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, Type> extension,
|
||||
final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Set the value of one element of a repeated extension. */
|
||||
public final <Type> BuilderType setExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final int index, final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.setRepeatedField(extension.descriptor, index, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Append a value to a repeated extension. */
|
||||
public final <Type> BuilderType addExtension(
|
||||
final GeneratedExtension<MessageType, List<Type>> extension,
|
||||
final Type value) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Clear an extension. */
|
||||
public final <Type> BuilderType clearExtension(
|
||||
final GeneratedExtension<MessageType, ?> extension) {
|
||||
verifyExtensionContainingType(extension);
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.clearField(extension.descriptor);
|
||||
return (BuilderType) this;
|
||||
}
|
||||
|
||||
/** Called by subclasses to check if all extensions are initialized. */
|
||||
protected boolean extensionsAreInitialized() {
|
||||
return extensions.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by subclasses to parse an unknown field or an extension.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
@Override
|
||||
protected boolean parseUnknownField(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
ensureExtensionsIsMutable();
|
||||
return GeneratedMessageLite.parseUnknownField(
|
||||
extensions,
|
||||
getDefaultInstanceForType(),
|
||||
input,
|
||||
extensionRegistry,
|
||||
tag);
|
||||
}
|
||||
|
||||
protected final void mergeExtensionFields(final MessageType other) {
|
||||
ensureExtensionsIsMutable();
|
||||
extensions.mergeFrom(((ExtendableMessage) other).extensions);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parse an unknown field or an extension.
|
||||
* @return {@code true} unless the tag is an end-group tag.
|
||||
*/
|
||||
private static <MessageType extends MessageLite>
|
||||
boolean parseUnknownField(
|
||||
FieldSet<ExtensionDescriptor> extensions,
|
||||
MessageType defaultInstance,
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry,
|
||||
int tag) throws IOException {
|
||||
int wireType = WireFormat.getTagWireType(tag);
|
||||
int fieldNumber = WireFormat.getTagFieldNumber(tag);
|
||||
|
||||
GeneratedExtension<MessageType, ?> extension =
|
||||
extensionRegistry.findLiteExtensionByNumber(
|
||||
defaultInstance, fieldNumber);
|
||||
|
||||
boolean unknown = false;
|
||||
boolean packed = false;
|
||||
if (extension == null) {
|
||||
unknown = true; // Unknown field.
|
||||
} else if (wireType == FieldSet.getWireFormatForFieldType(
|
||||
extension.descriptor.getLiteType(),
|
||||
false /* isPacked */)) {
|
||||
packed = false; // Normal, unpacked value.
|
||||
} else if (extension.descriptor.isRepeated &&
|
||||
extension.descriptor.type.isPackable() &&
|
||||
wireType == FieldSet.getWireFormatForFieldType(
|
||||
extension.descriptor.getLiteType(),
|
||||
true /* isPacked */)) {
|
||||
packed = true; // Packed value.
|
||||
} else {
|
||||
unknown = true; // Wrong wire type.
|
||||
}
|
||||
|
||||
if (unknown) { // Unknown field or wrong wire type. Skip.
|
||||
return input.skipField(tag);
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
int length = input.readRawVarint32();
|
||||
int limit = input.pushLimit(length);
|
||||
if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
int rawValue = input.readEnum();
|
||||
Object value =
|
||||
extension.descriptor.getEnumType().findValueByNumber(rawValue);
|
||||
if (value == null) {
|
||||
// If the number isn't recognized as a valid value for this
|
||||
// enum, drop it (don't even add it to unknownFields).
|
||||
return true;
|
||||
}
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
} else {
|
||||
while (input.getBytesUntilLimit() > 0) {
|
||||
Object value =
|
||||
FieldSet.readPrimitiveField(input,
|
||||
extension.descriptor.getLiteType());
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
input.popLimit(limit);
|
||||
} else {
|
||||
Object value;
|
||||
switch (extension.descriptor.getLiteJavaType()) {
|
||||
case MESSAGE: {
|
||||
MessageLite.Builder subBuilder = null;
|
||||
if (!extension.descriptor.isRepeated()) {
|
||||
MessageLite existingValue =
|
||||
(MessageLite) extensions.getField(extension.descriptor);
|
||||
if (existingValue != null) {
|
||||
subBuilder = existingValue.toBuilder();
|
||||
}
|
||||
}
|
||||
if (subBuilder == null) {
|
||||
subBuilder = extension.messageDefaultInstance.newBuilderForType();
|
||||
}
|
||||
if (extension.descriptor.getLiteType() ==
|
||||
WireFormat.FieldType.GROUP) {
|
||||
input.readGroup(extension.getNumber(),
|
||||
subBuilder, extensionRegistry);
|
||||
} else {
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
}
|
||||
value = subBuilder.build();
|
||||
break;
|
||||
}
|
||||
case ENUM:
|
||||
int rawValue = input.readEnum();
|
||||
value = extension.descriptor.getEnumType()
|
||||
.findValueByNumber(rawValue);
|
||||
// If the number isn't recognized as a valid value for this enum,
|
||||
// drop it.
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = FieldSet.readPrimitiveField(input,
|
||||
extension.descriptor.getLiteType());
|
||||
break;
|
||||
}
|
||||
|
||||
if (extension.descriptor.isRepeated()) {
|
||||
extensions.addRepeatedField(extension.descriptor, value);
|
||||
} else {
|
||||
extensions.setField(extension.descriptor, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
GeneratedExtension<ContainingType, Type>
|
||||
newSingularGeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type) {
|
||||
return new GeneratedExtension<ContainingType, Type>(
|
||||
containingTypeDefaultInstance,
|
||||
defaultValue,
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(enumTypeMap, number, type,
|
||||
false /* isRepeated */,
|
||||
false /* isPacked */));
|
||||
}
|
||||
|
||||
/** For use by generated code only. */
|
||||
public static <ContainingType extends MessageLite, Type>
|
||||
GeneratedExtension<ContainingType, Type>
|
||||
newRepeatedGeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isPacked) {
|
||||
@SuppressWarnings("unchecked") // Subclasses ensure Type is a List
|
||||
Type emptyList = (Type) Collections.emptyList();
|
||||
return new GeneratedExtension<ContainingType, Type>(
|
||||
containingTypeDefaultInstance,
|
||||
emptyList,
|
||||
messageDefaultInstance,
|
||||
new ExtensionDescriptor(
|
||||
enumTypeMap, number, type, true /* isRepeated */, isPacked));
|
||||
}
|
||||
|
||||
private static final class ExtensionDescriptor
|
||||
implements FieldSet.FieldDescriptorLite<
|
||||
ExtensionDescriptor> {
|
||||
private ExtensionDescriptor(
|
||||
final Internal.EnumLiteMap<?> enumTypeMap,
|
||||
final int number,
|
||||
final WireFormat.FieldType type,
|
||||
final boolean isRepeated,
|
||||
final boolean isPacked) {
|
||||
this.enumTypeMap = enumTypeMap;
|
||||
this.number = number;
|
||||
this.type = type;
|
||||
this.isRepeated = isRepeated;
|
||||
this.isPacked = isPacked;
|
||||
}
|
||||
|
||||
private final Internal.EnumLiteMap<?> enumTypeMap;
|
||||
private final int number;
|
||||
private final WireFormat.FieldType type;
|
||||
private final boolean isRepeated;
|
||||
private final boolean isPacked;
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public WireFormat.FieldType getLiteType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public WireFormat.JavaType getLiteJavaType() {
|
||||
return type.getJavaType();
|
||||
}
|
||||
|
||||
public boolean isRepeated() {
|
||||
return isRepeated;
|
||||
}
|
||||
|
||||
public boolean isPacked() {
|
||||
return isPacked;
|
||||
}
|
||||
|
||||
public Internal.EnumLiteMap<?> getEnumType() {
|
||||
return enumTypeMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public MessageLite.Builder internalMergeFrom(
|
||||
MessageLite.Builder to, MessageLite from) {
|
||||
return ((Builder) to).mergeFrom((GeneratedMessageLite) from);
|
||||
}
|
||||
|
||||
public int compareTo(ExtensionDescriptor other) {
|
||||
return number - other.number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link GeneratedMessage.GeneratedExtension}.
|
||||
*
|
||||
* Users should ignore the contents of this class and only use objects of
|
||||
* this type as parameters to extension accessors and ExtensionRegistry.add().
|
||||
*/
|
||||
public static final class GeneratedExtension<
|
||||
ContainingType extends MessageLite, Type> {
|
||||
|
||||
private GeneratedExtension(
|
||||
final ContainingType containingTypeDefaultInstance,
|
||||
final Type defaultValue,
|
||||
final MessageLite messageDefaultInstance,
|
||||
final ExtensionDescriptor descriptor) {
|
||||
// Defensive checks to verify the correct initialization order of
|
||||
// GeneratedExtensions and their related GeneratedMessages.
|
||||
if (containingTypeDefaultInstance == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Null containingTypeDefaultInstance");
|
||||
}
|
||||
if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE &&
|
||||
messageDefaultInstance == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Null messageDefaultInstance");
|
||||
}
|
||||
this.containingTypeDefaultInstance = containingTypeDefaultInstance;
|
||||
this.defaultValue = defaultValue;
|
||||
this.messageDefaultInstance = messageDefaultInstance;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
private final ContainingType containingTypeDefaultInstance;
|
||||
private final Type defaultValue;
|
||||
private final MessageLite messageDefaultInstance;
|
||||
private final ExtensionDescriptor descriptor;
|
||||
|
||||
/**
|
||||
* Default instance of the type being extended, used to identify that type.
|
||||
*/
|
||||
public ContainingType getContainingTypeDefaultInstance() {
|
||||
return containingTypeDefaultInstance;
|
||||
}
|
||||
|
||||
/** Get the field number. */
|
||||
public int getNumber() {
|
||||
return descriptor.getNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the extension is an embedded message, this is the default instance of
|
||||
* that type.
|
||||
*/
|
||||
public MessageLite getMessageDefaultInstance() {
|
||||
return messageDefaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A serialized (serializable) form of the generated message. Stores the
|
||||
* message as a class name and a byte array.
|
||||
*/
|
||||
static final class SerializedForm implements Serializable {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private String messageClassName;
|
||||
private byte[] asBytes;
|
||||
|
||||
/**
|
||||
* Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}.
|
||||
* @param regularForm the message to serialize
|
||||
*/
|
||||
SerializedForm(MessageLite regularForm) {
|
||||
messageClassName = regularForm.getClass().getName();
|
||||
asBytes = regularForm.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* When read from an ObjectInputStream, this method converts this object
|
||||
* back to the regular form. Part of Java's serialization magic.
|
||||
* @return a GeneratedMessage of the type that was serialized
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object readResolve() throws ObjectStreamException {
|
||||
try {
|
||||
Class messageClass = Class.forName(messageClassName);
|
||||
Method newBuilder = messageClass.getMethod("newBuilder");
|
||||
MessageLite.Builder builder =
|
||||
(MessageLite.Builder) newBuilder.invoke(null);
|
||||
builder.mergeFrom(asBytes);
|
||||
return builder.buildPartial();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Unable to find proto buffer class", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Unable to find newBuilder method", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Unable to call newBuilder method", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException("Error calling newBuilder", e.getCause());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new RuntimeException("Unable to understand proto buffer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces this object in the output stream with a serialized form.
|
||||
* Part of Java's serialization magic. Generated sub-classes must override
|
||||
* this method by calling {@code return super.writeReplace();}
|
||||
* @return a SerializedForm of this message
|
||||
*/
|
||||
protected Object writeReplace() throws ObjectStreamException {
|
||||
return new SerializedForm(this);
|
||||
}
|
||||
}
|
||||
153
java/src/main/java/com/google/protobuf/Internal.java
Normal file
153
java/src/main/java/com/google/protobuf/Internal.java
Normal file
@@ -0,0 +1,153 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* The classes contained within are used internally by the Protocol Buffer
|
||||
* library and generated message implementations. They are public only because
|
||||
* those generated messages do not reside in the {@code protobuf} package.
|
||||
* Others should not use this class directly.
|
||||
*
|
||||
* @author kenton@google.com (Kenton Varda)
|
||||
*/
|
||||
public class Internal {
|
||||
/**
|
||||
* Helper called by generated code to construct default values for string
|
||||
* fields.
|
||||
* <p>
|
||||
* The protocol compiler does not actually contain a UTF-8 decoder -- it
|
||||
* just pushes UTF-8-encoded text around without touching it. The one place
|
||||
* where this presents a problem is when generating Java string literals.
|
||||
* Unicode characters in the string literal would normally need to be encoded
|
||||
* using a Unicode escape sequence, which would require decoding them.
|
||||
* To get around this, protoc instead embeds the UTF-8 bytes into the
|
||||
* generated code and leaves it to the runtime library to decode them.
|
||||
* <p>
|
||||
* It gets worse, though. If protoc just generated a byte array, like:
|
||||
* new byte[] {0x12, 0x34, 0x56, 0x78}
|
||||
* Java actually generates *code* which allocates an array and then fills
|
||||
* in each value. This is much less efficient than just embedding the bytes
|
||||
* directly into the bytecode. To get around this, we need another
|
||||
* work-around. String literals are embedded directly, so protoc actually
|
||||
* generates a string literal corresponding to the bytes. The easiest way
|
||||
* to do this is to use the ISO-8859-1 character set, which corresponds to
|
||||
* the first 256 characters of the Unicode range. Protoc can then use
|
||||
* good old CEscape to generate the string.
|
||||
* <p>
|
||||
* So we have a string literal which represents a set of bytes which
|
||||
* represents another string. This function -- stringDefaultValue --
|
||||
* converts from the generated string to the string we actually want. The
|
||||
* generated code calls this automatically.
|
||||
*/
|
||||
public static String stringDefaultValue(String bytes) {
|
||||
try {
|
||||
return new String(bytes.getBytes("ISO-8859-1"), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// This should never happen since all JVMs are required to implement
|
||||
// both of the above character sets.
|
||||
throw new IllegalStateException(
|
||||
"Java VM does not support a standard character set.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to construct default values for bytes
|
||||
* fields.
|
||||
* <p>
|
||||
* This is a lot like {@link #stringDefaultValue}, but for bytes fields.
|
||||
* In this case we only need the second of the two hacks -- allowing us to
|
||||
* embed raw bytes as a string literal with ISO-8859-1 encoding.
|
||||
*/
|
||||
public static ByteString bytesDefaultValue(String bytes) {
|
||||
try {
|
||||
return ByteString.copyFrom(bytes.getBytes("ISO-8859-1"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// This should never happen since all JVMs are required to implement
|
||||
// ISO-8859-1.
|
||||
throw new IllegalStateException(
|
||||
"Java VM does not support a standard character set.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper called by generated code to determine if a byte array is a valid
|
||||
* UTF-8 encoded string such that the original bytes can be converted to
|
||||
* a String object and then back to a byte array round tripping the bytes
|
||||
* without loss. More precisely, returns {@code true} whenever:
|
||||
* <pre> {@code
|
||||
* Arrays.equals(byteString.toByteArray(),
|
||||
* new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method rejects "overlong" byte sequences, as well as
|
||||
* 3-byte sequences that would map to a surrogate character, in
|
||||
* accordance with the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1. Note that the UTF-8 decoder included in Oracle's
|
||||
* JDK has been modified to also reject "overlong" byte sequences,
|
||||
* but currently (2011) still accepts 3-byte surrogate character
|
||||
* byte sequences.
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* <p>As of 2011-02, this method simply returns the result of {@link
|
||||
* ByteString#isValidUtf8()}. Calling that method directly is preferred.
|
||||
*
|
||||
* @param byteString the string to check
|
||||
* @return whether the byte array is round trippable
|
||||
*/
|
||||
public static boolean isValidUtf8(ByteString byteString) {
|
||||
return byteString.isValidUtf8();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for an enum value or value descriptor, to be used in FieldSet.
|
||||
* The lite library stores enum values directly in FieldSets but the full
|
||||
* library stores EnumValueDescriptors in order to better support reflection.
|
||||
*/
|
||||
public interface EnumLite {
|
||||
int getNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for an object which maps integers to {@link EnumLite}s.
|
||||
* {@link Descriptors.EnumDescriptor} implements this interface by mapping
|
||||
* numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally,
|
||||
* every generated enum type has a static method internalGetValueMap() which
|
||||
* returns an implementation of this type that maps numbers to enum values.
|
||||
*/
|
||||
public interface EnumLiteMap<T extends EnumLite> {
|
||||
T findValueByNumber(int number);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when a protocol message being parsed is invalid in some way,
|
||||
* e.g. it contains a malformed varint or a negative byte length.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class InvalidProtocolBufferException extends IOException {
|
||||
private static final long serialVersionUID = -1616151763072450476L;
|
||||
private MessageLite unfinishedMessage = null;
|
||||
|
||||
public InvalidProtocolBufferException(final String description) {
|
||||
super(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches an unfinished message to the exception to support best-effort
|
||||
* parsing in {@code Parser} interface.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public InvalidProtocolBufferException setUnfinishedMessage(
|
||||
MessageLite unfinishedMessage) {
|
||||
this.unfinishedMessage = unfinishedMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unfinished message attached to the exception, or null if
|
||||
* no message is attached.
|
||||
*/
|
||||
public MessageLite getUnfinishedMessage() {
|
||||
return unfinishedMessage;
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException truncatedMessage() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"While parsing a protocol message, the input ended unexpectedly " +
|
||||
"in the middle of a field. This could mean either than the " +
|
||||
"input has been truncated or that an embedded message " +
|
||||
"misreported its own length.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException negativeSize() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"CodedInputStream encountered an embedded string or message " +
|
||||
"which claimed to have negative size.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException malformedVarint() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"CodedInputStream encountered a malformed varint.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidTag() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message contained an invalid tag (zero).");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidEndTag() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message end-group tag did not match expected tag.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException invalidWireType() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message tag had invalid wire type.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException recursionLimitExceeded() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message had too many levels of nesting. May be malicious. " +
|
||||
"Use CodedInputStream.setRecursionLimit() to increase the depth limit.");
|
||||
}
|
||||
|
||||
static InvalidProtocolBufferException sizeLimitExceeded() {
|
||||
return new InvalidProtocolBufferException(
|
||||
"Protocol message was too large. May be malicious. " +
|
||||
"Use CodedInputStream.setSizeLimit() to increase the size limit.");
|
||||
}
|
||||
}
|
||||
210
java/src/main/java/com/google/protobuf/LazyField.java
Normal file
210
java/src/main/java/com/google/protobuf/LazyField.java
Normal file
@@ -0,0 +1,210 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* LazyField encapsulates the logic of lazily parsing message fields. It stores
|
||||
* the message in a ByteString initially and then parse it on-demand.
|
||||
*
|
||||
* LazyField is thread-compatible e.g. concurrent read are safe, however,
|
||||
* synchronizations are needed under read/write situations.
|
||||
*
|
||||
* Now LazyField is only used to lazily load MessageSet.
|
||||
* TODO(xiangl): Use LazyField to lazily load all messages.
|
||||
*
|
||||
* @author xiangl@google.com (Xiang Li)
|
||||
*/
|
||||
class LazyField {
|
||||
|
||||
final private MessageLite defaultInstance;
|
||||
final private ExtensionRegistryLite extensionRegistry;
|
||||
|
||||
// Mutable because it is initialized lazily.
|
||||
private ByteString bytes;
|
||||
private volatile MessageLite value;
|
||||
private volatile boolean isDirty = false;
|
||||
|
||||
public LazyField(MessageLite defaultInstance,
|
||||
ExtensionRegistryLite extensionRegistry, ByteString bytes) {
|
||||
this.defaultInstance = defaultInstance;
|
||||
this.extensionRegistry = extensionRegistry;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public MessageLite getValue() {
|
||||
ensureInitialized();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* LazyField is not thread-safe for write access. Synchronizations are needed
|
||||
* under read/write situations.
|
||||
*/
|
||||
public MessageLite setValue(MessageLite value) {
|
||||
MessageLite originalValue = this.value;
|
||||
this.value = value;
|
||||
bytes = null;
|
||||
isDirty = true;
|
||||
return originalValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the optional field can be duplicated at the end of serialized
|
||||
* bytes, which will make the serialized size changed after LazyField
|
||||
* parsed. Be careful when using this method.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
if (isDirty) {
|
||||
return value.getSerializedSize();
|
||||
}
|
||||
return bytes.size();
|
||||
}
|
||||
|
||||
public ByteString toByteString() {
|
||||
if (!isDirty) {
|
||||
return bytes;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!isDirty) {
|
||||
return bytes;
|
||||
}
|
||||
bytes = value.toByteString();
|
||||
isDirty = false;
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
ensureInitialized();
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
ensureInitialized();
|
||||
return value.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ensureInitialized();
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
if (value != null) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (value != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (bytes != null) {
|
||||
value = defaultInstance.getParserForType()
|
||||
.parseFrom(bytes, extensionRegistry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// TODO(xiangl): Refactory the API to support the exception thrown from
|
||||
// lazily load messages.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
|
||||
/**
|
||||
* LazyEntry and LazyIterator are used to encapsulate the LazyField, when
|
||||
* users iterate all fields from FieldSet.
|
||||
*/
|
||||
static class LazyEntry<K> implements Entry<K, Object> {
|
||||
private Entry<K, LazyField> entry;
|
||||
|
||||
private LazyEntry(Entry<K, LazyField> entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
LazyField field = entry.getValue();
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
return field.getValue();
|
||||
}
|
||||
|
||||
public LazyField getField() {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
public Object setValue(Object value) {
|
||||
if (!(value instanceof MessageLite)) {
|
||||
throw new IllegalArgumentException(
|
||||
"LazyField now only used for MessageSet, "
|
||||
+ "and the value of MessageSet must be an instance of MessageLite");
|
||||
}
|
||||
return entry.getValue().setValue((MessageLite) value);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyIterator<K> implements Iterator<Entry<K, Object>> {
|
||||
private Iterator<Entry<K, Object>> iterator;
|
||||
|
||||
public LazyIterator(Iterator<Entry<K, Object>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Entry<K, Object> next() {
|
||||
Entry<K, ?> entry = iterator.next();
|
||||
if (entry.getValue() instanceof LazyField) {
|
||||
return new LazyEntry<K>((Entry<K, LazyField>) entry);
|
||||
}
|
||||
return (Entry<K, Object>) entry;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
178
java/src/main/java/com/google/protobuf/LazyStringArrayList.java
Normal file
178
java/src/main/java/com/google/protobuf/LazyStringArrayList.java
Normal file
@@ -0,0 +1,178 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps an ArrayList. Each
|
||||
* element is either a ByteString or a String. It caches the last one requested
|
||||
* which is most likely the one needed next. This minimizes memory usage while
|
||||
* satisfying the most common use cases.
|
||||
* <p>
|
||||
* <strong>Note that this implementation is not synchronized.</strong>
|
||||
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
|
||||
* and at least one of the threads modifies the list structurally, it
|
||||
* <i>must</i> be synchronized externally. (A structural modification is
|
||||
* any operation that adds or deletes one or more elements, or explicitly
|
||||
* resizes the backing array; merely setting the value of an element is not
|
||||
* a structural modification.) This is typically accomplished by
|
||||
* synchronizing on some object that naturally encapsulates the list.
|
||||
* <p>
|
||||
* If the implementation is accessed via concurrent reads, this is thread safe.
|
||||
* Conversions are done in a thread safe manner. It's possible that the
|
||||
* conversion may happen more than once if two threads attempt to access the
|
||||
* same element and the modifications were not visible to each other, but this
|
||||
* will not result in any corruption of the list or change in behavior other
|
||||
* than performance.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringArrayList extends AbstractList<String>
|
||||
implements LazyStringList, RandomAccess {
|
||||
|
||||
public final static LazyStringList EMPTY = new UnmodifiableLazyStringList(
|
||||
new LazyStringArrayList());
|
||||
|
||||
private final List<Object> list;
|
||||
|
||||
public LazyStringArrayList() {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public LazyStringArrayList(LazyStringList from) {
|
||||
list = new ArrayList<Object>(from.size());
|
||||
addAll(from);
|
||||
}
|
||||
|
||||
public LazyStringArrayList(List<String> from) {
|
||||
list = new ArrayList<Object>(from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
Object o = list.get(index);
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
ByteString bs = (ByteString) o;
|
||||
String s = bs.toStringUtf8();
|
||||
if (bs.isValidUtf8()) {
|
||||
list.set(index, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String set(int index, String s) {
|
||||
Object o = list.set(index, s);
|
||||
return asString(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, String element) {
|
||||
list.add(index, element);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends String> c) {
|
||||
// The default implementation of AbstractCollection.addAll(Collection)
|
||||
// delegates to add(Object). This implementation instead delegates to
|
||||
// addAll(int, Collection), which makes a special case for Collections
|
||||
// which are instances of LazyStringList.
|
||||
return addAll(size(), c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends String> c) {
|
||||
// When copying from another LazyStringList, directly copy the underlying
|
||||
// elements rather than forcing each element to be decoded to a String.
|
||||
Collection<?> collection = c instanceof LazyStringList
|
||||
? ((LazyStringList) c).getUnderlyingElements() : c;
|
||||
boolean ret = list.addAll(index, collection);
|
||||
modCount++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(int index) {
|
||||
Object o = list.remove(index);
|
||||
modCount++;
|
||||
return asString(o);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
list.clear();
|
||||
modCount++;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void add(ByteString element) {
|
||||
list.add(element);
|
||||
modCount++;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public ByteString getByteString(int index) {
|
||||
Object o = list.get(index);
|
||||
if (o instanceof String) {
|
||||
ByteString b = ByteString.copyFromUtf8((String) o);
|
||||
list.set(index, b);
|
||||
return b;
|
||||
} else {
|
||||
return (ByteString) o;
|
||||
}
|
||||
}
|
||||
|
||||
private String asString(Object o) {
|
||||
if (o instanceof String) {
|
||||
return (String) o;
|
||||
} else {
|
||||
return ((ByteString) o).toStringUtf8();
|
||||
}
|
||||
}
|
||||
|
||||
public List<?> getUnderlyingElements() {
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
}
|
||||
81
java/src/main/java/com/google/protobuf/LazyStringList.java
Normal file
81
java/src/main/java/com/google/protobuf/LazyStringList.java
Normal file
@@ -0,0 +1,81 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface extending {@code List<String>} that also provides access to the
|
||||
* items of the list as UTF8-encoded ByteString objects. This is used by the
|
||||
* protocol buffer implementation to support lazily converting bytes parsed
|
||||
* over the wire to String objects until needed and also increases the
|
||||
* efficiency of serialization if the String was never requested as the
|
||||
* ByteString is already cached.
|
||||
* <p>
|
||||
* This only adds additional methods that are required for the use in the
|
||||
* protocol buffer code in order to be able successfully round trip byte arrays
|
||||
* through parsing and serialization without conversion to strings. It's not
|
||||
* attempting to support the functionality of say {@code List<ByteString>}, hence
|
||||
* why only these two very specific methods are added.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface LazyStringList extends List<String> {
|
||||
|
||||
/**
|
||||
* Returns the element at the specified position in this list as a ByteString.
|
||||
*
|
||||
* @param index index of the element to return
|
||||
* @return the element at the specified position in this list
|
||||
* @throws IndexOutOfBoundsException if the index is out of range
|
||||
* ({@code index < 0 || index >= size()})
|
||||
*/
|
||||
ByteString getByteString(int index);
|
||||
|
||||
/**
|
||||
* Appends the specified element to the end of this list (optional
|
||||
* operation).
|
||||
*
|
||||
* @param element element to be appended to this list
|
||||
* @throws UnsupportedOperationException if the <tt>add</tt> operation
|
||||
* is not supported by this list
|
||||
*/
|
||||
void add(ByteString element);
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable List of the underlying elements, each of
|
||||
* which is either a {@code String} or its equivalent UTF-8 encoded
|
||||
* {@code ByteString}. It is an error for the caller to modify the returned
|
||||
* List, and attempting to do so will result in an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*/
|
||||
List<?> getUnderlyingElements();
|
||||
}
|
||||
349
java/src/main/java/com/google/protobuf/LiteralByteString.java
Normal file
349
java/src/main/java/com/google/protobuf/LiteralByteString.java
Normal file
@@ -0,0 +1,349 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This class implements a {@link com.google.protobuf.ByteString} backed by a
|
||||
* single array of bytes, contiguous in memory. It supports substring by
|
||||
* pointing to only a sub-range of the underlying byte array, meaning that a
|
||||
* substring will reference the full byte-array of the string it's made from,
|
||||
* exactly as with {@link String}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class LiteralByteString extends ByteString {
|
||||
|
||||
protected final byte[] bytes;
|
||||
|
||||
/**
|
||||
* Creates a {@code LiteralByteString} backed by the given array, without
|
||||
* copying.
|
||||
*
|
||||
* @param bytes array to wrap
|
||||
*/
|
||||
LiteralByteString(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
// Unlike most methods in this class, this one is a direct implementation
|
||||
// ignoring the potential offset because we need to do range-checking in the
|
||||
// substring case anyway.
|
||||
return bytes[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> substring
|
||||
|
||||
@Override
|
||||
public ByteString substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index: " + beginIndex + " < 0");
|
||||
}
|
||||
if (endIndex > size()) {
|
||||
throw new IndexOutOfBoundsException("End index: " + endIndex + " > " +
|
||||
size());
|
||||
}
|
||||
int substringLength = endIndex - beginIndex;
|
||||
if (substringLength < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index larger than ending index: " + beginIndex + ", "
|
||||
+ endIndex);
|
||||
}
|
||||
|
||||
ByteString result;
|
||||
if (substringLength == 0) {
|
||||
result = ByteString.EMPTY;
|
||||
} else {
|
||||
result = new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex,
|
||||
substringLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
// Optimized form, not for subclasses, since we don't call
|
||||
// getOffsetIntoBytes() or check the 'numberToCopy' parameter.
|
||||
System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(ByteBuffer target) {
|
||||
target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer =
|
||||
ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size());
|
||||
return byteBuffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Return the ByteBuffer generated by asReadOnlyByteBuffer() as a singleton
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>(1);
|
||||
result.add(asReadOnlyByteBuffer());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
outputStream.write(toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(bytes, getOffsetIntoBytes(), size(), charsetName);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
int offset = getOffsetIntoBytes();
|
||||
return Utf8.isValidUtf8(bytes, offset, offset + size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
int index = getOffsetIntoBytes() + offset;
|
||||
return Utf8.partialIsValidUtf8(state, bytes, index, index + length);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size() != ((ByteString) other).size()) {
|
||||
return false;
|
||||
}
|
||||
if (size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (other instanceof LiteralByteString) {
|
||||
return equalsRange((LiteralByteString) other, 0, size());
|
||||
} else if (other instanceof RopeByteString) {
|
||||
return other.equals(this);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Has a new type of ByteString been created? Found "
|
||||
+ other.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check equality of the substring of given length of this object starting at
|
||||
* zero with another {@code LiteralByteString} substring starting at offset.
|
||||
*
|
||||
* @param other what to compare a substring in
|
||||
* @param offset offset into other
|
||||
* @param length number of bytes to compare
|
||||
* @return true for equality of substrings, else false.
|
||||
*/
|
||||
boolean equalsRange(LiteralByteString other, int offset, int length) {
|
||||
if (length > other.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Length too large: " + length + size());
|
||||
}
|
||||
if (offset + length > other.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Ran off end of other: " + offset + ", " + length + ", " +
|
||||
other.size());
|
||||
}
|
||||
|
||||
byte[] thisBytes = bytes;
|
||||
byte[] otherBytes = other.bytes;
|
||||
int thisLimit = getOffsetIntoBytes() + length;
|
||||
for (int thisIndex = getOffsetIntoBytes(), otherIndex =
|
||||
other.getOffsetIntoBytes() + offset;
|
||||
(thisIndex < thisLimit); ++thisIndex, ++otherIndex) {
|
||||
if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which
|
||||
* is safe because of the Java Memory Model's "no out-of-thin-air values"
|
||||
* guarantees for ints.
|
||||
*/
|
||||
private int hash = 0;
|
||||
|
||||
/**
|
||||
* Compute the hashCode using the traditional algorithm from {@link
|
||||
* ByteString}.
|
||||
*
|
||||
* @return hashCode value
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
|
||||
if (h == 0) {
|
||||
int size = size();
|
||||
h = partialHash(size, 0, size);
|
||||
if (h == 0) {
|
||||
h = 1;
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int peekCachedHashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialHash(int h, int offset, int length) {
|
||||
byte[] thisBytes = bytes;
|
||||
for (int i = getOffsetIntoBytes() + offset, limit = i + length; i < limit;
|
||||
i++) {
|
||||
h = h * 31 + thisBytes[i];
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
@Override
|
||||
public InputStream newInput() {
|
||||
return new ByteArrayInputStream(bytes, getOffsetIntoBytes(),
|
||||
size()); // No copy
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodedInputStream newCodedInput() {
|
||||
// We trust CodedInputStream not to modify the bytes, or to give anyone
|
||||
// else access to them.
|
||||
return CodedInputStream
|
||||
.newInstance(bytes, getOffsetIntoBytes(), size()); // No copy
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new LiteralByteIterator();
|
||||
}
|
||||
|
||||
private class LiteralByteIterator implements ByteIterator {
|
||||
private int position;
|
||||
private final int limit;
|
||||
|
||||
private LiteralByteIterator() {
|
||||
position = 0;
|
||||
limit = size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (position < limit);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
// Boxing calls Byte.valueOf(byte), which does not instantiate.
|
||||
return nextByte();
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
try {
|
||||
return bytes[position++];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new NoSuchElementException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Internal methods
|
||||
|
||||
@Override
|
||||
protected int getTreeDepth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isBalanced() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset into {@code bytes[]} to use, non-zero for substrings.
|
||||
*
|
||||
* @return always 0 for this class
|
||||
*/
|
||||
protected int getOffsetIntoBytes() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
237
java/src/main/java/com/google/protobuf/Message.java
Normal file
237
java/src/main/java/com/google/protobuf/Message.java
Normal file
@@ -0,0 +1,237 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
|
||||
// mergeFrom*() could return BuilderType for better type-safety.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message objects.
|
||||
* <p>
|
||||
* See also {@link MessageLite}, which defines most of the methods that typical
|
||||
* users care about. {@link Message} adds to it methods that are not available
|
||||
* in the "lite" runtime. The biggest added features are introspection and
|
||||
* reflection -- i.e., getting descriptors for the message type and accessing
|
||||
* the field values dynamically.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Message extends MessageLite, MessageOrBuilder {
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Parser<? extends Message> getParserForType();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Comparison and hashing
|
||||
|
||||
/**
|
||||
* Compares the specified object with this message for equality. Returns
|
||||
* {@code true} if the given object is a message of the same type (as
|
||||
* defined by {@code getDescriptorForType()}) and has identical values for
|
||||
* all of its fields. Subclasses must implement this; inheriting
|
||||
* {@code Object.equals()} is incorrect.
|
||||
*
|
||||
* @param other object to be compared for equality with this message
|
||||
* @return {@code true} if the specified object is equal to this message
|
||||
*/
|
||||
@Override
|
||||
boolean equals(Object other);
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this message. The hash code of a message
|
||||
* should mix the message's type (object identity of the descriptor) with its
|
||||
* contents (known and unknown field values). Subclasses must implement this;
|
||||
* inheriting {@code Object.hashCode()} is incorrect.
|
||||
*
|
||||
* @return the hash code value for this message
|
||||
* @see Map#hashCode()
|
||||
*/
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Converts the message to a string in protocol buffer text format. This is
|
||||
* just a trivial wrapper around {@link
|
||||
* TextFormat#printToString(MessageOrBuilder)}.
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
// =================================================================
|
||||
// Builders
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
Builder newBuilderForType();
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLite.Builder, MessageOrBuilder {
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder clear();
|
||||
|
||||
/**
|
||||
* Merge {@code other} into the message being built. {@code other} must
|
||||
* have the exact same type as {@code this} (i.e.
|
||||
* {@code getDescriptorForType() == other.getDescriptorForType()}).
|
||||
*
|
||||
* Merging occurs as follows. For each field:<br>
|
||||
* * For singular primitive fields, if the field is set in {@code other},
|
||||
* then {@code other}'s value overwrites the value in this message.<br>
|
||||
* * For singular message fields, if the field is set in {@code other},
|
||||
* it is merged into the corresponding sub-message of this message
|
||||
* using the same merging rules.<br>
|
||||
* * For repeated fields, the elements in {@code other} are concatenated
|
||||
* with the elements in this message.
|
||||
*
|
||||
* This is equivalent to the {@code Message::MergeFrom} method in C++.
|
||||
*/
|
||||
Builder mergeFrom(Message other);
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Message build();
|
||||
Message buildPartial();
|
||||
Builder clone();
|
||||
Builder mergeFrom(CodedInputStream input) throws IOException;
|
||||
Builder mergeFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the message's type's descriptor.
|
||||
* See {@link Message#getDescriptorForType()}.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Create a Builder for messages of the appropriate type for the given
|
||||
* field. Messages built with this can then be passed to setField(),
|
||||
* setRepeatedField(), or addRepeatedField().
|
||||
*/
|
||||
Builder newBuilderForField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Get a nested builder instance for the given field.
|
||||
* <p>
|
||||
* Normally, we hold a reference to the immutable message object for the
|
||||
* message type field. Some implementations(the generated message builders),
|
||||
* however, can also hold a reference to the builder object (a nested
|
||||
* builder) for the field.
|
||||
* <p>
|
||||
* If the field is already backed up by a nested builder, the nested builder
|
||||
* will be returned. Otherwise, a new field builder will be created and
|
||||
* returned. The original message field (if exist) will be merged into the
|
||||
* field builder, which will then be nested into its parent builder.
|
||||
* <p>
|
||||
* NOTE: implementations that do not support nested builders will throw
|
||||
* <code>UnsupportedException</code>.
|
||||
*/
|
||||
Builder getFieldBuilder(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets a field to the given value. The value must be of the correct type
|
||||
* for this field, i.e. the same type that
|
||||
* {@link Message#getField(Descriptors.FieldDescriptor)} would return.
|
||||
*/
|
||||
Builder setField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/**
|
||||
* Clears the field. This is exactly equivalent to calling the generated
|
||||
* "clear" accessor method corresponding to the field.
|
||||
*/
|
||||
Builder clearField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Sets an element of a repeated field to the given value. The value must
|
||||
* be of the correct type for this field, i.e. the same type that
|
||||
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would
|
||||
* return.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Builder setRepeatedField(Descriptors.FieldDescriptor field,
|
||||
int index, Object value);
|
||||
|
||||
/**
|
||||
* Like {@code setRepeatedField}, but appends the value as a new element.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value);
|
||||
|
||||
/** Set the {@link UnknownFieldSet} for this message. */
|
||||
Builder setUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
/**
|
||||
* Merge some unknown fields into the {@link UnknownFieldSet} for this
|
||||
* message.
|
||||
*/
|
||||
Builder mergeUnknownFields(UnknownFieldSet unknownFields);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
// (From MessageLite.Builder, re-declared here only for return type
|
||||
// covariance.)
|
||||
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
Builder mergeFrom(InputStream input) throws IOException;
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException;
|
||||
boolean mergeDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
}
|
||||
}
|
||||
319
java/src/main/java/com/google/protobuf/MessageLite.java
Normal file
319
java/src/main/java/com/google/protobuf/MessageLite.java
Normal file
@@ -0,0 +1,319 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
|
||||
// mergeFrom*() could return BuilderType for better type-safety.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message objects.
|
||||
*
|
||||
* <p>This interface is implemented by all protocol message objects. Non-lite
|
||||
* messages additionally implement the Message interface, which is a subclass
|
||||
* of MessageLite. Use MessageLite instead when you only need the subset of
|
||||
* features which it supports -- namely, nothing that uses descriptors or
|
||||
* reflection. You can instruct the protocol compiler to generate classes
|
||||
* which implement only MessageLite, not the full Message interface, by adding
|
||||
* the follow line to the .proto file:
|
||||
* <pre>
|
||||
* option optimize_for = LITE_RUNTIME;
|
||||
* </pre>
|
||||
*
|
||||
* <p>This is particularly useful on resource-constrained systems where the
|
||||
* full protocol buffers runtime library is too big.
|
||||
*
|
||||
* <p>Note that on non-constrained systems (e.g. servers) when you need to link
|
||||
* in lots of protocol definitions, a better way to reduce total code footprint
|
||||
* is to use {@code optimize_for = CODE_SIZE}. This will make the generated
|
||||
* code smaller while still supporting all the same features (at the expense of
|
||||
* speed). {@code optimize_for = LITE_RUNTIME} is best when you only have a
|
||||
* small number of message types linked into your binary, in which case the
|
||||
* size of the protocol buffers runtime itself is the biggest problem.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface MessageLite extends MessageLiteOrBuilder {
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This does not
|
||||
* flush or close the stream.
|
||||
*/
|
||||
void writeTo(CodedOutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this message. The result
|
||||
* is only computed on the first call and memoized after that.
|
||||
*/
|
||||
int getSerializedSize();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parser for a message of the same type as this message.
|
||||
*/
|
||||
Parser<? extends MessageLite> getParserForType();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code ByteString} and returns it. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
ByteString toByteString();
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code byte} array and returns it. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
byte[] toByteArray();
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This is just a
|
||||
* trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does
|
||||
* not flush or close the stream.
|
||||
* <p>
|
||||
* NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write
|
||||
* any more data to the stream after the message, you must somehow ensure
|
||||
* that the parser on the receiving end does not interpret this as being
|
||||
* part of the protocol message. This can be done e.g. by writing the size
|
||||
* of the message before the data, then making sure to limit the input to
|
||||
* that size on the receiving end (e.g. by wrapping the InputStream in one
|
||||
* which limits the input). Alternatively, just use
|
||||
* {@link #writeDelimitedTo(OutputStream)}.
|
||||
*/
|
||||
void writeTo(OutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #writeTo(OutputStream)}, but writes the size of the message
|
||||
* as a varint before writing the data. This allows more data to be written
|
||||
* to the stream after the message without the need to delimit the message
|
||||
* data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or
|
||||
* the static method {@code YourMessageType.parseDelimitedFrom(InputStream)})
|
||||
* to parse messages written by this method.
|
||||
*/
|
||||
void writeDelimitedTo(OutputStream output) throws IOException;
|
||||
|
||||
// =================================================================
|
||||
// Builders
|
||||
|
||||
/**
|
||||
* Constructs a new builder for a message of the same type as this message.
|
||||
*/
|
||||
Builder newBuilderForType();
|
||||
|
||||
/**
|
||||
* Constructs a builder initialized with the current message. Use this to
|
||||
* derive a new message from the current one.
|
||||
*/
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Abstract interface implemented by Protocol Message builders.
|
||||
*/
|
||||
interface Builder extends MessageLiteOrBuilder, Cloneable {
|
||||
/** Resets all fields to their default values. */
|
||||
Builder clear();
|
||||
|
||||
/**
|
||||
* Constructs the message based on the state of the Builder. Subsequent
|
||||
* changes to the Builder will not affect the returned message.
|
||||
* @throws UninitializedMessageException The message is missing one or more
|
||||
* required fields (i.e. {@link #isInitialized()} returns false).
|
||||
* Use {@link #buildPartial()} to bypass this check.
|
||||
*/
|
||||
MessageLite build();
|
||||
|
||||
/**
|
||||
* Like {@link #build()}, but does not throw an exception if the message
|
||||
* is missing required fields. Instead, a partial message is returned.
|
||||
* Subsequent changes to the Builder will not affect the returned message.
|
||||
*/
|
||||
MessageLite buildPartial();
|
||||
|
||||
/**
|
||||
* Clones the Builder.
|
||||
* @see Object#clone()
|
||||
*/
|
||||
Builder clone();
|
||||
|
||||
/**
|
||||
* Parses a message of this type from the input and merges it with this
|
||||
* message.
|
||||
*
|
||||
* <p>Warning: This does not verify that all required fields are present in
|
||||
* the input message. If you call {@link #build()} without setting all
|
||||
* required fields, it will throw an {@link UninitializedMessageException},
|
||||
* which is a {@code RuntimeException} and thus might not be caught. There
|
||||
* are a few good ways to deal with this:
|
||||
* <ul>
|
||||
* <li>Call {@link #isInitialized()} to verify that all required fields
|
||||
* are set before building.
|
||||
* <li>Use {@code buildPartial()} to build, which ignores missing
|
||||
* required fields.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note: The caller should call
|
||||
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
|
||||
* verify that the last tag seen was the appropriate end-group tag,
|
||||
* or zero for EOF.
|
||||
*/
|
||||
Builder mergeFrom(CodedInputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link Builder#mergeFrom(CodedInputStream)}, but also
|
||||
* parses extensions. The extensions that you want to be able to parse
|
||||
* must be registered in {@code extensionRegistry}. Extensions not in
|
||||
* the registry will be treated as unknown fields.
|
||||
*/
|
||||
Builder mergeFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse {@code data} as a message of this type and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse a message of this type from {@code input} and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}. Note that this method always
|
||||
* reads the <i>entire</i> input (unless it throws an exception). If you
|
||||
* want it to stop earlier, you will need to wrap your input in some
|
||||
* wrapper stream that limits reading. Or, use
|
||||
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write your message
|
||||
* and {@link #mergeDelimitedFrom(InputStream)} to read it.
|
||||
* <p>
|
||||
* Despite usually reading the entire input, this does not close the stream.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Parse a message of this type from {@code input} and merge it with the
|
||||
* message being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
Builder mergeFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #mergeFrom(InputStream)}, but does not read until EOF.
|
||||
* Instead, the size of the message (encoded as a varint) is read first,
|
||||
* then the message data. Use
|
||||
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in
|
||||
* this format.
|
||||
*
|
||||
* @return True if successful, or false if the stream is at EOF when the
|
||||
* method starts. Any other error (including reaching EOF during
|
||||
* parsing) will cause an exception to be thrown.
|
||||
*/
|
||||
boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions.
|
||||
*/
|
||||
boolean mergeDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Base interface for methods common to {@link MessageLite}
|
||||
* and {@link MessageLite.Builder} to provide type equivalency.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface MessageLiteOrBuilder {
|
||||
/**
|
||||
* Get an instance of the type with no fields set. Because no fields are set,
|
||||
* all getters for singular fields will return default values and repeated
|
||||
* fields will appear empty.
|
||||
* This may or may not be a singleton. This differs from the
|
||||
* {@code getDefaultInstance()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code MessageLite} interface
|
||||
* whereas {@code getDefaultInstance()} is a static method of a specific
|
||||
* class. They return the same thing.
|
||||
*/
|
||||
MessageLite getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Returns true if all required fields in the message and all embedded
|
||||
* messages are set, false otherwise.
|
||||
*
|
||||
* <p>See also: {@link MessageOrBuilder#getInitializationErrorString()}
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
}
|
||||
129
java/src/main/java/com/google/protobuf/MessageOrBuilder.java
Normal file
129
java/src/main/java/com/google/protobuf/MessageOrBuilder.java
Normal file
@@ -0,0 +1,129 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base interface for methods common to {@link Message} and
|
||||
* {@link Message.Builder} to provide type equivalency.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public interface MessageOrBuilder extends MessageLiteOrBuilder {
|
||||
|
||||
// (From MessageLite, re-declared here only for return type covariance.)
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
Message getDefaultInstanceForType();
|
||||
|
||||
/**
|
||||
* Returns a list of field paths (e.g. "foo.bar.baz") of required fields
|
||||
* which are not set in this message. You should call
|
||||
* {@link MessageLiteOrBuilder#isInitialized()} first to check if there
|
||||
* are any missing fields, as that method is likely to be much faster
|
||||
* than this one even when the message is fully-initialized.
|
||||
*/
|
||||
List<String> findInitializationErrors();
|
||||
|
||||
/**
|
||||
* Returns a comma-delimited list of required fields which are not set
|
||||
* in this message object. You should call
|
||||
* {@link MessageLiteOrBuilder#isInitialized()} first to check if there
|
||||
* are any missing fields, as that method is likely to be much faster
|
||||
* than this one even when the message is fully-initialized.
|
||||
*/
|
||||
String getInitializationErrorString();
|
||||
|
||||
/**
|
||||
* Get the message's type's descriptor. This differs from the
|
||||
* {@code getDescriptor()} method of generated message classes in that
|
||||
* this method is an abstract method of the {@code Message} interface
|
||||
* whereas {@code getDescriptor()} is a static method of a specific class.
|
||||
* They return the same thing.
|
||||
*/
|
||||
Descriptors.Descriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* Returns a collection of all the fields in this message which are set
|
||||
* and their corresponding values. A singular ("required" or "optional")
|
||||
* field is set iff hasField() returns true for that field. A "repeated"
|
||||
* field is set iff getRepeatedFieldSize() is greater than zero. The
|
||||
* values are exactly what would be returned by calling
|
||||
* {@link #getField(Descriptors.FieldDescriptor)} for each field. The map
|
||||
* is guaranteed to be a sorted map, so iterating over it will return fields
|
||||
* in order by field number.
|
||||
* <br>
|
||||
* If this is for a builder, the returned map may or may not reflect future
|
||||
* changes to the builder. Either way, the returned map is itself
|
||||
* unmodifiable.
|
||||
*/
|
||||
Map<Descriptors.FieldDescriptor, Object> getAllFields();
|
||||
|
||||
/**
|
||||
* Returns true if the given field is set. This is exactly equivalent to
|
||||
* calling the generated "has" accessor method corresponding to the field.
|
||||
* @throws IllegalArgumentException The field is a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
boolean hasField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Obtains the value of the given field, or the default value if it is
|
||||
* not set. For primitive fields, the boxed primitive value is returned.
|
||||
* For enum fields, the EnumValueDescriptor for the value is returned. For
|
||||
* embedded message fields, the sub-message is returned. For repeated
|
||||
* fields, a java.util.List is returned.
|
||||
*/
|
||||
Object getField(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets the number of elements of a repeated field. This is exactly
|
||||
* equivalent to calling the generated "Count" accessor method corresponding
|
||||
* to the field.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
|
||||
|
||||
/**
|
||||
* Gets an element of a repeated field. For primitive fields, the boxed
|
||||
* primitive value is returned. For enum fields, the EnumValueDescriptor
|
||||
* for the value is returned. For embedded message fields, the sub-message
|
||||
* is returned.
|
||||
* @throws IllegalArgumentException The field is not a repeated field, or
|
||||
* {@code field.getContainingType() != getDescriptorForType()}.
|
||||
*/
|
||||
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
|
||||
|
||||
/** Get the {@link UnknownFieldSet} for this message. */
|
||||
UnknownFieldSet getUnknownFields();
|
||||
}
|
||||
259
java/src/main/java/com/google/protobuf/Parser.java
Normal file
259
java/src/main/java/com/google/protobuf/Parser.java
Normal file
@@ -0,0 +1,259 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Abstract interface for parsing Protocol Messages.
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public interface Parser<MessageType> {
|
||||
/**
|
||||
* Parses a message of {@code MessageType} from the input.
|
||||
*
|
||||
* <p>Note: The caller should call
|
||||
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
|
||||
* verify that the last tag seen was the appropriate end-group tag,
|
||||
* or zero for EOF.
|
||||
*/
|
||||
public MessageType parseFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream)}, but also parses extensions.
|
||||
* The extensions that you want to be able to parse must be registered in
|
||||
* {@code extensionRegistry}. Extensions not in the registry will be treated
|
||||
* as unknown fields.
|
||||
*/
|
||||
public MessageType parseFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(CodedInputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(CodedInputStream input, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Convenience methods.
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(ByteString data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses {@code data} as a message of {@code MessageType}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[], int, int)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(ByteString, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[])}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(byte[], ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parse a message of {@code MessageType} from {@code input}.
|
||||
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
|
||||
* Note that this method always reads the <i>entire</i> input (unless it
|
||||
* throws an exception). If you want it to stop earlier, you will need to
|
||||
* wrap your input in some wrapper stream that limits reading. Or, use
|
||||
* {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write your
|
||||
* message and {@link #parseDelimitedFrom(InputStream)} to read it.
|
||||
* <p>
|
||||
* Despite usually reading the entire input, this does not close the stream.
|
||||
*/
|
||||
public MessageType parseFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Parses a message of {@code MessageType} from {@code input}.
|
||||
* This is just a small wrapper around
|
||||
* {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
|
||||
*/
|
||||
public MessageType parseFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseFrom(InputStream)}, but does not read util EOF.
|
||||
* Instead, the size of message (encoded as a varint) is read first,
|
||||
* then the message data. Use
|
||||
* {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write
|
||||
* messages in this format.
|
||||
*
|
||||
* @return True if successful, or false if the stream is at EOF when the
|
||||
* method starts. Any other error (including reaching EOF during
|
||||
* parsing) will cause an exception to be thrown.
|
||||
*/
|
||||
public MessageType parseDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream)} but supporting extensions.
|
||||
*/
|
||||
public MessageType parseDelimitedFrom(InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream)}, but does not throw an
|
||||
* exception if the message is missing required fields. Instead, a partial
|
||||
* message is returned.
|
||||
*/
|
||||
public MessageType parsePartialDelimitedFrom(InputStream input)
|
||||
throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Like {@link #parseDelimitedFrom(InputStream, ExtensionRegistryLite)},
|
||||
* but does not throw an exception if the message is missing required fields.
|
||||
* Instead, a partial message is returned.
|
||||
*/
|
||||
public MessageType parsePartialDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.EnumDescriptor;
|
||||
import com.google.protobuf.Descriptors.EnumValueDescriptor;
|
||||
|
||||
/**
|
||||
* Interface of useful methods added to all enums generated by the protocol
|
||||
* compiler.
|
||||
*/
|
||||
public interface ProtocolMessageEnum extends Internal.EnumLite {
|
||||
|
||||
/**
|
||||
* Return the value's numeric value as defined in the .proto file.
|
||||
*/
|
||||
int getNumber();
|
||||
|
||||
/**
|
||||
* Return the value's descriptor, which contains information such as
|
||||
* value name, number, and type.
|
||||
*/
|
||||
EnumValueDescriptor getValueDescriptor();
|
||||
|
||||
/**
|
||||
* Return the enum type's descriptor, which contains information
|
||||
* about each defined value, etc.
|
||||
*/
|
||||
EnumDescriptor getDescriptorForType();
|
||||
}
|
||||
696
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
Normal file
696
java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
Normal file
@@ -0,0 +1,696 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@code RepeatedFieldBuilder} implements a structure that a protocol
|
||||
* message uses to hold a repeated field of other protocol messages. It supports
|
||||
* the classical use case of adding immutable {@link Message}'s to the
|
||||
* repeated field and is highly optimized around this (no extra memory
|
||||
* allocations and sharing of immutable arrays).
|
||||
* <br>
|
||||
* It also supports the additional use case of adding a {@link Message.Builder}
|
||||
* to the repeated field and deferring conversion of that {@code Builder}
|
||||
* to an immutable {@code Message}. In this way, it's possible to maintain
|
||||
* a tree of {@code Builder}'s that acts as a fully read/write data
|
||||
* structure.
|
||||
* <br>
|
||||
* Logically, one can think of a tree of builders as converting the entire tree
|
||||
* to messages when build is called on the root or when any method is called
|
||||
* that desires a Message instead of a Builder. In terms of the implementation,
|
||||
* the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
|
||||
* classes cache messages that were created so that messages only need to be
|
||||
* created when some change occured in its builder or a builder for one of its
|
||||
* descendants.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class RepeatedFieldBuilder
|
||||
<MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
// Parent to send changes to.
|
||||
private GeneratedMessage.BuilderParent parent;
|
||||
|
||||
// List of messages. Never null. It may be immutable, in which case
|
||||
// isMessagesListImmutable will be true. See note below.
|
||||
private List<MType> messages;
|
||||
|
||||
// Whether messages is an mutable array that can be modified.
|
||||
private boolean isMessagesListMutable;
|
||||
|
||||
// List of builders. May be null, in which case, no nested builders were
|
||||
// created. If not null, entries represent the builder for that index.
|
||||
private List<SingleFieldBuilder<MType, BType, IType>> builders;
|
||||
|
||||
// Here are the invariants for messages and builders:
|
||||
// 1. messages is never null and its count corresponds to the number of items
|
||||
// in the repeated field.
|
||||
// 2. If builders is non-null, messages and builders MUST always
|
||||
// contain the same number of items.
|
||||
// 3. Entries in either array can be null, but for any index, there MUST be
|
||||
// either a Message in messages or a builder in builders.
|
||||
// 4. If the builder at an index is non-null, the builder is
|
||||
// authoritative. This is the case where a Builder was set on the index.
|
||||
// Any message in the messages array MUST be ignored.
|
||||
// t. If the builder at an index is null, the message in the messages
|
||||
// list is authoritative. This is the case where a Message (not a Builder)
|
||||
// was set directly for an index.
|
||||
|
||||
// Indicates that we've built a message and so we are now obligated
|
||||
// to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
|
||||
private boolean isClean;
|
||||
|
||||
// A view of this builder that exposes a List interface of messages. This is
|
||||
// initialized on demand. This is fully backed by this object and all changes
|
||||
// are reflected in it. Access to any item converts it to a message if it
|
||||
// was a builder.
|
||||
private MessageExternalList<MType, BType, IType> externalMessageList;
|
||||
|
||||
// A view of this builder that exposes a List interface of builders. This is
|
||||
// initialized on demand. This is fully backed by this object and all changes
|
||||
// are reflected in it. Access to any item converts it to a builder if it
|
||||
// was a message.
|
||||
private BuilderExternalList<MType, BType, IType> externalBuilderList;
|
||||
|
||||
// A view of this builder that exposes a List interface of the interface
|
||||
// implemented by messages and builders. This is initialized on demand. This
|
||||
// is fully backed by this object and all changes are reflected in it.
|
||||
// Access to any item returns either a builder or message depending on
|
||||
// what is most efficient.
|
||||
private MessageOrBuilderExternalList<MType, BType, IType>
|
||||
externalMessageOrBuilderList;
|
||||
|
||||
/**
|
||||
* Constructs a new builder with an empty list of messages.
|
||||
*
|
||||
* @param messages the current list of messages
|
||||
* @param isMessagesListMutable Whether the messages list is mutable
|
||||
* @param parent a listener to notify of changes
|
||||
* @param isClean whether the builder is initially marked clean
|
||||
*/
|
||||
public RepeatedFieldBuilder(
|
||||
List<MType> messages,
|
||||
boolean isMessagesListMutable,
|
||||
GeneratedMessage.BuilderParent parent,
|
||||
boolean isClean) {
|
||||
this.messages = messages;
|
||||
this.isMessagesListMutable = isMessagesListMutable;
|
||||
this.parent = parent;
|
||||
this.isClean = isClean;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// Null out parent so we stop sending it invalidations.
|
||||
parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the list of messages is mutable so it can be updated. If it's
|
||||
* immutable, a copy is made.
|
||||
*/
|
||||
private void ensureMutableMessageList() {
|
||||
if (!isMessagesListMutable) {
|
||||
messages = new ArrayList<MType>(messages);
|
||||
isMessagesListMutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the list of builders is not null. If it's null, the list is
|
||||
* created and initialized to be the same size as the messages list with
|
||||
* null entries.
|
||||
*/
|
||||
private void ensureBuilders() {
|
||||
if (this.builders == null) {
|
||||
this.builders =
|
||||
new ArrayList<SingleFieldBuilder<MType, BType, IType>>(
|
||||
messages.size());
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
builders.add(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of items in the list.
|
||||
*
|
||||
* @return the count of items in the list.
|
||||
*/
|
||||
public int getCount() {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the list is empty.
|
||||
*
|
||||
* @return whether the list is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return messages.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message at the specified index. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return the message for the specified index
|
||||
*/
|
||||
public MType getMessage(int index) {
|
||||
return getMessage(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message at the specified index. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @param forBuild this is being called for build so we want to make sure
|
||||
* we SingleFieldBuilder.build to send dirty invalidations
|
||||
* @return the message for the specified index
|
||||
*/
|
||||
private MType getMessage(int index, boolean forBuild) {
|
||||
if (this.builders == null) {
|
||||
// We don't have any builders -- return the current Message.
|
||||
// This is the case where no builder was created, so we MUST have a
|
||||
// Message.
|
||||
return messages.get(index);
|
||||
}
|
||||
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
// We don't have a builder -- return the current message.
|
||||
// This is the case where no builder was created for the entry at index,
|
||||
// so we MUST have a message.
|
||||
return messages.get(index);
|
||||
|
||||
} else {
|
||||
return forBuild ? builder.build() : builder.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a builder for the specified index. If no builder has been created for
|
||||
* that index, a builder is created on demand by calling
|
||||
* {@link Message#toBuilder}.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return The builder for that index
|
||||
*/
|
||||
public BType getBuilder(int index) {
|
||||
ensureBuilders();
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
MType message = messages.get(index);
|
||||
builder = new SingleFieldBuilder<MType, BType, IType>(
|
||||
message, this, isClean);
|
||||
builders.set(index, builder);
|
||||
}
|
||||
return builder.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base class interface for the specified index. This may either be
|
||||
* a builder or a message. It will return whatever is more efficient.
|
||||
*
|
||||
* @param index the index of the message to get
|
||||
* @return the message or builder for the index as the base class interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public IType getMessageOrBuilder(int index) {
|
||||
if (this.builders == null) {
|
||||
// We don't have any builders -- return the current Message.
|
||||
// This is the case where no builder was created, so we MUST have a
|
||||
// Message.
|
||||
return (IType) messages.get(index);
|
||||
}
|
||||
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
|
||||
if (builder == null) {
|
||||
// We don't have a builder -- return the current message.
|
||||
// This is the case where no builder was created for the entry at index,
|
||||
// so we MUST have a message.
|
||||
return (IType) messages.get(index);
|
||||
|
||||
} else {
|
||||
return builder.getMessageOrBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a message at the specified index replacing the existing item at
|
||||
* that index.
|
||||
*
|
||||
* @param index the index to set.
|
||||
* @param message the message to set
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> setMessage(
|
||||
int index, MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.set(index, message);
|
||||
if (builders != null) {
|
||||
SingleFieldBuilder<MType, BType, IType> entry =
|
||||
builders.set(index, null);
|
||||
if (entry != null) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified element to the end of this list.
|
||||
*
|
||||
* @param message the message to add
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> addMessage(
|
||||
MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.add(message);
|
||||
if (builders != null) {
|
||||
builders.add(null);
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the specified message at the specified position in this list.
|
||||
* Shifts the element currently at that position (if any) and any subsequent
|
||||
* elements to the right (adds one to their indices).
|
||||
*
|
||||
* @param index the index at which to insert the message
|
||||
* @param message the message to add
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> addMessage(
|
||||
int index, MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
messages.add(index, message);
|
||||
if (builders != null) {
|
||||
builders.add(index, null);
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends all of the messages in the specified collection to the end of
|
||||
* this list, in the order that they are returned by the specified
|
||||
* collection's iterator.
|
||||
*
|
||||
* @param values the messages to add
|
||||
* @return the builder
|
||||
*/
|
||||
public RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
|
||||
Iterable<? extends MType> values) {
|
||||
for (final MType value : values) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
if (values instanceof Collection) {
|
||||
@SuppressWarnings("unchecked") final
|
||||
Collection<MType> collection = (Collection<MType>) values;
|
||||
if (collection.size() == 0) {
|
||||
return this;
|
||||
}
|
||||
ensureMutableMessageList();
|
||||
for (MType value : values) {
|
||||
addMessage(value);
|
||||
}
|
||||
} else {
|
||||
ensureMutableMessageList();
|
||||
for (MType value : values) {
|
||||
addMessage(value);
|
||||
}
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new builder to the end of this list and returns the builder.
|
||||
*
|
||||
* @param message the message to add which is the basis of the builder
|
||||
* @return the new builder
|
||||
*/
|
||||
public BType addBuilder(MType message) {
|
||||
ensureMutableMessageList();
|
||||
ensureBuilders();
|
||||
SingleFieldBuilder<MType, BType, IType> builder =
|
||||
new SingleFieldBuilder<MType, BType, IType>(
|
||||
message, this, isClean);
|
||||
messages.add(null);
|
||||
builders.add(builder);
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return builder.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new builder at the specified position in this list.
|
||||
* Shifts the element currently at that position (if any) and any subsequent
|
||||
* elements to the right (adds one to their indices).
|
||||
*
|
||||
* @param index the index at which to insert the builder
|
||||
* @param message the message to add which is the basis of the builder
|
||||
* @return the builder
|
||||
*/
|
||||
public BType addBuilder(int index, MType message) {
|
||||
ensureMutableMessageList();
|
||||
ensureBuilders();
|
||||
SingleFieldBuilder<MType, BType, IType> builder =
|
||||
new SingleFieldBuilder<MType, BType, IType>(
|
||||
message, this, isClean);
|
||||
messages.add(index, null);
|
||||
builders.add(index, builder);
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
return builder.getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the element at the specified position in this list. Shifts any
|
||||
* subsequent elements to the left (subtracts one from their indices).
|
||||
* Returns the element that was removed from the list.
|
||||
*
|
||||
* @param index the index at which to remove the message
|
||||
*/
|
||||
public void remove(int index) {
|
||||
ensureMutableMessageList();
|
||||
messages.remove(index);
|
||||
if (builders != null) {
|
||||
SingleFieldBuilder<MType, BType, IType> entry =
|
||||
builders.remove(index);
|
||||
if (entry != null) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the elements from this list.
|
||||
* The list will be empty after this call returns.
|
||||
*/
|
||||
public void clear() {
|
||||
messages = Collections.emptyList();
|
||||
isMessagesListMutable = false;
|
||||
if (builders != null) {
|
||||
for (SingleFieldBuilder<MType, BType, IType> entry :
|
||||
builders) {
|
||||
if (entry != null) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
builders = null;
|
||||
}
|
||||
onChanged();
|
||||
incrementModCounts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the list of messages from the builder and returns them.
|
||||
*
|
||||
* @return an immutable list of messages
|
||||
*/
|
||||
public List<MType> build() {
|
||||
// Now that build has been called, we are required to dispatch
|
||||
// invalidations.
|
||||
isClean = true;
|
||||
|
||||
if (!isMessagesListMutable && builders == null) {
|
||||
// We still have an immutable list and we never created a builder.
|
||||
return messages;
|
||||
}
|
||||
|
||||
boolean allMessagesInSync = true;
|
||||
if (!isMessagesListMutable) {
|
||||
// We still have an immutable list. Let's see if any of them are out
|
||||
// of sync with their builders.
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
Message message = messages.get(i);
|
||||
SingleFieldBuilder<MType, BType, IType> builder = builders.get(i);
|
||||
if (builder != null) {
|
||||
if (builder.build() != message) {
|
||||
allMessagesInSync = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allMessagesInSync) {
|
||||
// Immutable list is still in sync.
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to make sure messages is up to date
|
||||
ensureMutableMessageList();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
messages.set(i, getMessage(i, true));
|
||||
}
|
||||
|
||||
// We're going to return our list as immutable so we mark that we can
|
||||
// no longer update it.
|
||||
messages = Collections.unmodifiableList(messages);
|
||||
isMessagesListMutable = false;
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a view of the builder as a list of messages. The returned list is live
|
||||
* and will reflect any changes to the underlying builder.
|
||||
*
|
||||
* @return the messages in the list
|
||||
*/
|
||||
public List<MType> getMessageList() {
|
||||
if (externalMessageList == null) {
|
||||
externalMessageList =
|
||||
new MessageExternalList<MType, BType, IType>(this);
|
||||
}
|
||||
return externalMessageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a view of the builder as a list of builders. This returned list is
|
||||
* live and will reflect any changes to the underlying builder.
|
||||
*
|
||||
* @return the builders in the list
|
||||
*/
|
||||
public List<BType> getBuilderList() {
|
||||
if (externalBuilderList == null) {
|
||||
externalBuilderList =
|
||||
new BuilderExternalList<MType, BType, IType>(this);
|
||||
}
|
||||
return externalBuilderList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a view of the builder as a list of MessageOrBuilders. This returned
|
||||
* list is live and will reflect any changes to the underlying builder.
|
||||
*
|
||||
* @return the builders in the list
|
||||
*/
|
||||
public List<IType> getMessageOrBuilderList() {
|
||||
if (externalMessageOrBuilderList == null) {
|
||||
externalMessageOrBuilderList =
|
||||
new MessageOrBuilderExternalList<MType, BType, IType>(this);
|
||||
}
|
||||
return externalMessageOrBuilderList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a the builder or one of its nested children has changed
|
||||
* and any parent should be notified of its invalidation.
|
||||
*/
|
||||
private void onChanged() {
|
||||
if (isClean && parent != null) {
|
||||
parent.markDirty();
|
||||
|
||||
// Don't keep dispatching invalidations until build is called again.
|
||||
isClean = false;
|
||||
}
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void markDirty() {
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the mod counts so that an ConcurrentModificationException can
|
||||
* be thrown if calling code tries to modify the builder while its iterating
|
||||
* the list.
|
||||
*/
|
||||
private void incrementModCounts() {
|
||||
if (externalMessageList != null) {
|
||||
externalMessageList.incrementModCount();
|
||||
}
|
||||
if (externalBuilderList != null) {
|
||||
externalBuilderList.incrementModCount();
|
||||
}
|
||||
if (externalMessageOrBuilderList != null) {
|
||||
externalMessageOrBuilderList.incrementModCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a live view of the builder as a list of messages.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*/
|
||||
private static class MessageExternalList<
|
||||
MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<MType> implements List<MType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
MessageExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public MType get(int index) {
|
||||
return builder.getMessage(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a live view of the builder as a list of builders.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*/
|
||||
private static class BuilderExternalList<
|
||||
MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<BType> implements List<BType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
BuilderExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public BType get(int index) {
|
||||
return builder.getBuilder(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a live view of the builder as a list of builders.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*/
|
||||
private static class MessageOrBuilderExternalList<
|
||||
MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
extends AbstractList<IType> implements List<IType> {
|
||||
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder;
|
||||
|
||||
MessageOrBuilderExternalList(
|
||||
RepeatedFieldBuilder<MType, BType, IType> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.builder.getCount();
|
||||
}
|
||||
|
||||
public IType get(int index) {
|
||||
return builder.getMessageOrBuilder(index);
|
||||
}
|
||||
|
||||
void incrementModCount() {
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
943
java/src/main/java/com/google/protobuf/RopeByteString.java
Normal file
943
java/src/main/java/com/google/protobuf/RopeByteString.java
Normal file
@@ -0,0 +1,943 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Class to represent {@code ByteStrings} formed by concatenation of other
|
||||
* ByteStrings, without copying the data in the pieces. The concatenation is
|
||||
* represented as a tree whose leaf nodes are each a {@link LiteralByteString}.
|
||||
*
|
||||
* <p>Most of the operation here is inspired by the now-famous paper <a
|
||||
* href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and
|
||||
* michael plass
|
||||
*
|
||||
* <p>The algorithms described in the paper have been implemented for character
|
||||
* strings in {@link com.google.common.string.Rope} and in the c++ class {@code
|
||||
* cord.cc}.
|
||||
*
|
||||
* <p>Fundamentally the Rope algorithm represents the collection of pieces as a
|
||||
* binary tree. BAP95 uses a Fibonacci bound relating depth to a minimum
|
||||
* sequence length, sequences that are too short relative to their depth cause a
|
||||
* tree rebalance. More precisely, a tree of depth d is "balanced" in the
|
||||
* terminology of BAP95 if its length is at least F(d+2), where F(n) is the
|
||||
* n-the Fibonacci number. Thus for depths 0, 1, 2, 3, 4, 5,... we have minimum
|
||||
* lengths 1, 2, 3, 5, 8, 13,...
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
class RopeByteString extends ByteString {
|
||||
|
||||
/**
|
||||
* BAP95. Let Fn be the nth Fibonacci number. A {@link RopeByteString} of
|
||||
* depth n is "balanced", i.e flat enough, if its length is at least Fn+2,
|
||||
* e.g. a "balanced" {@link RopeByteString} of depth 1 must have length at
|
||||
* least 2, of depth 4 must have length >= 8, etc.
|
||||
*
|
||||
* <p>There's nothing special about using the Fibonacci numbers for this, but
|
||||
* they are a reasonable sequence for encapsulating the idea that we are OK
|
||||
* with longer strings being encoded in deeper binary trees.
|
||||
*
|
||||
* <p>For 32-bit integers, this array has length 46.
|
||||
*/
|
||||
private static final int[] minLengthByDepth;
|
||||
|
||||
static {
|
||||
// Dynamically generate the list of Fibonacci numbers the first time this
|
||||
// class is accessed.
|
||||
List<Integer> numbers = new ArrayList<Integer>();
|
||||
|
||||
// we skip the first Fibonacci number (1). So instead of: 1 1 2 3 5 8 ...
|
||||
// we have: 1 2 3 5 8 ...
|
||||
int f1 = 1;
|
||||
int f2 = 1;
|
||||
|
||||
// get all the values until we roll over.
|
||||
while (f2 > 0) {
|
||||
numbers.add(f2);
|
||||
int temp = f1 + f2;
|
||||
f1 = f2;
|
||||
f2 = temp;
|
||||
}
|
||||
|
||||
// we include this here so that we can index this array to [x + 1] in the
|
||||
// loops below.
|
||||
numbers.add(Integer.MAX_VALUE);
|
||||
minLengthByDepth = new int[numbers.size()];
|
||||
for (int i = 0; i < minLengthByDepth.length; i++) {
|
||||
// unbox all the values
|
||||
minLengthByDepth[i] = numbers.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
private final int totalLength;
|
||||
private final ByteString left;
|
||||
private final ByteString right;
|
||||
private final int leftLength;
|
||||
private final int treeDepth;
|
||||
|
||||
/**
|
||||
* Create a new RopeByteString, which can be thought of as a new tree node, by
|
||||
* recording references to the two given strings.
|
||||
*
|
||||
* @param left string on the left of this node, should have {@code size() >
|
||||
* 0}
|
||||
* @param right string on the right of this node, should have {@code size() >
|
||||
* 0}
|
||||
*/
|
||||
private RopeByteString(ByteString left, ByteString right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
leftLength = left.size();
|
||||
totalLength = leftLength + right.size();
|
||||
treeDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate the given strings while performing various optimizations to
|
||||
* slow the growth rate of tree depth and tree node count. The result is
|
||||
* either a {@link LiteralByteString} or a {@link RopeByteString}
|
||||
* depending on which optimizations, if any, were applied.
|
||||
*
|
||||
* <p>Small pieces of length less than {@link
|
||||
* ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in
|
||||
* BAP95. Large pieces are referenced without copy.
|
||||
*
|
||||
* @param left string on the left
|
||||
* @param right string on the right
|
||||
* @return concatenation representing the same sequence as the given strings
|
||||
*/
|
||||
static ByteString concatenate(ByteString left, ByteString right) {
|
||||
ByteString result;
|
||||
RopeByteString leftRope =
|
||||
(left instanceof RopeByteString) ? (RopeByteString) left : null;
|
||||
if (right.size() == 0) {
|
||||
result = left;
|
||||
} else if (left.size() == 0) {
|
||||
result = right;
|
||||
} else {
|
||||
int newLength = left.size() + right.size();
|
||||
if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
|
||||
// Optimization from BAP95: For short (leaves in paper, but just short
|
||||
// here) total length, do a copy of data to a new leaf.
|
||||
result = concatenateBytes(left, right);
|
||||
} else if (leftRope != null
|
||||
&& leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
|
||||
// Optimization from BAP95: As an optimization of the case where the
|
||||
// ByteString is constructed by repeated concatenate, recognize the case
|
||||
// where a short string is concatenated to a left-hand node whose
|
||||
// right-hand branch is short. In the paper this applies to leaves, but
|
||||
// we just look at the length here. This has the advantage of shedding
|
||||
// references to unneeded data when substrings have been taken.
|
||||
//
|
||||
// When we recognize this case, we do a copy of the data and create a
|
||||
// new parent node so that the depth of the result is the same as the
|
||||
// given left tree.
|
||||
ByteString newRight = concatenateBytes(leftRope.right, right);
|
||||
result = new RopeByteString(leftRope.left, newRight);
|
||||
} else if (leftRope != null
|
||||
&& leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
|
||||
&& leftRope.getTreeDepth() > right.getTreeDepth()) {
|
||||
// Typically for concatenate-built strings the left-side is deeper than
|
||||
// the right. This is our final attempt to concatenate without
|
||||
// increasing the tree depth. We'll redo the the node on the RHS. This
|
||||
// is yet another optimization for building the string by repeatedly
|
||||
// concatenating on the right.
|
||||
ByteString newRight = new RopeByteString(leftRope.right, right);
|
||||
result = new RopeByteString(leftRope.left, newRight);
|
||||
} else {
|
||||
// Fine, we'll add a node and increase the tree depth--unless we
|
||||
// rebalance ;^)
|
||||
int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
|
||||
if (newLength >= minLengthByDepth[newDepth]) {
|
||||
// The tree is shallow enough, so don't rebalance
|
||||
result = new RopeByteString(left, right);
|
||||
} else {
|
||||
result = new Balancer().balance(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates two strings by copying data values. This is called in a few
|
||||
* cases in order to reduce the growth of the number of tree nodes.
|
||||
*
|
||||
* @param left string on the left
|
||||
* @param right string on the right
|
||||
* @return string formed by copying data bytes
|
||||
*/
|
||||
private static LiteralByteString concatenateBytes(ByteString left,
|
||||
ByteString right) {
|
||||
int leftSize = left.size();
|
||||
int rightSize = right.size();
|
||||
byte[] bytes = new byte[leftSize + rightSize];
|
||||
left.copyTo(bytes, 0, 0, leftSize);
|
||||
right.copyTo(bytes, 0, leftSize, rightSize);
|
||||
return new LiteralByteString(bytes); // Constructor wraps bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RopeByteString for testing only while bypassing all the
|
||||
* defenses of {@link #concatenate(ByteString, ByteString)}. This allows
|
||||
* testing trees of specific structure. We are also able to insert empty
|
||||
* leaves, though these are dis-allowed, so that we can make sure the
|
||||
* implementation can withstand their presence.
|
||||
*
|
||||
* @param left string on the left of this node
|
||||
* @param right string on the right of this node
|
||||
* @return an unsafe instance for testing only
|
||||
*/
|
||||
static RopeByteString newInstanceForTest(ByteString left, ByteString right) {
|
||||
return new RopeByteString(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the byte at the given index.
|
||||
* Throws {@link ArrayIndexOutOfBoundsException} for backwards-compatibility
|
||||
* reasons although it would more properly be {@link
|
||||
* IndexOutOfBoundsException}.
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
|
||||
*/
|
||||
@Override
|
||||
public byte byteAt(int index) {
|
||||
if (index < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException("Index < 0: " + index);
|
||||
}
|
||||
if (index > totalLength) {
|
||||
throw new ArrayIndexOutOfBoundsException(
|
||||
"Index > length: " + index + ", " + totalLength);
|
||||
}
|
||||
|
||||
byte result;
|
||||
// Find the relevant piece by recursive descent
|
||||
if (index < leftLength) {
|
||||
result = left.byteAt(index);
|
||||
} else {
|
||||
result = right.byteAt(index - leftLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Pieces
|
||||
|
||||
@Override
|
||||
protected int getTreeDepth() {
|
||||
return treeDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the tree is balanced according to BAP95, which means the tree
|
||||
* is flat-enough with respect to the bounds. Note that this definition of
|
||||
* balanced is one where sub-trees of balanced trees are not necessarily
|
||||
* balanced.
|
||||
*
|
||||
* @return true if the tree is balanced
|
||||
*/
|
||||
@Override
|
||||
protected boolean isBalanced() {
|
||||
return totalLength >= minLengthByDepth[treeDepth];
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a substring of this one. This involves recursive descent along the
|
||||
* left and right edges of the substring, and referencing any wholly contained
|
||||
* segments in between. Any leaf nodes entirely uninvolved in the substring
|
||||
* will not be referenced by the substring.
|
||||
*
|
||||
* <p>Substrings of {@code length < 2} should result in at most a single
|
||||
* recursive call chain, terminating at a leaf node. Thus the result will be a
|
||||
* {@link LiteralByteString}. {@link #RopeByteString(ByteString,
|
||||
* ByteString)}.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
* @return substring leaf node or tree
|
||||
*/
|
||||
@Override
|
||||
public ByteString substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index: " + beginIndex + " < 0");
|
||||
}
|
||||
if (endIndex > totalLength) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"End index: " + endIndex + " > " + totalLength);
|
||||
}
|
||||
int substringLength = endIndex - beginIndex;
|
||||
if (substringLength < 0) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Beginning index larger than ending index: " + beginIndex + ", "
|
||||
+ endIndex);
|
||||
}
|
||||
|
||||
ByteString result;
|
||||
if (substringLength == 0) {
|
||||
// Empty substring
|
||||
result = ByteString.EMPTY;
|
||||
} else if (substringLength == totalLength) {
|
||||
// The whole string
|
||||
result = this;
|
||||
} else {
|
||||
// Proper substring
|
||||
if (endIndex <= leftLength) {
|
||||
// Substring on the left
|
||||
result = left.substring(beginIndex, endIndex);
|
||||
} else if (beginIndex >= leftLength) {
|
||||
// Substring on the right
|
||||
result = right
|
||||
.substring(beginIndex - leftLength, endIndex - leftLength);
|
||||
} else {
|
||||
// Split substring
|
||||
ByteString leftSub = left.substring(beginIndex);
|
||||
ByteString rightSub = right.substring(0, endIndex - leftLength);
|
||||
// Intentionally not rebalancing, since in many cases these two
|
||||
// substrings will already be less deep than the top-level
|
||||
// RopeByteString we're taking a substring of.
|
||||
result = new RopeByteString(leftSub, rightSub);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteString -> byte[]
|
||||
|
||||
@Override
|
||||
protected void copyToInternal(byte[] target, int sourceOffset,
|
||||
int targetOffset, int numberToCopy) {
|
||||
if (sourceOffset + numberToCopy <= leftLength) {
|
||||
left.copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
|
||||
} else if (sourceOffset >= leftLength) {
|
||||
right.copyToInternal(target, sourceOffset - leftLength, targetOffset,
|
||||
numberToCopy);
|
||||
} else {
|
||||
int leftLength = this.leftLength - sourceOffset;
|
||||
left.copyToInternal(target, sourceOffset, targetOffset, leftLength);
|
||||
right.copyToInternal(target, 0, targetOffset + leftLength,
|
||||
numberToCopy - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(ByteBuffer target) {
|
||||
left.copyTo(target);
|
||||
right.copyTo(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray());
|
||||
return byteBuffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Walk through the list of LiteralByteString's that make up this
|
||||
// rope, and add each one as a read-only ByteBuffer.
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>();
|
||||
PieceIterator pieces = new PieceIterator(this);
|
||||
while (pieces.hasNext()) {
|
||||
LiteralByteString byteString = pieces.next();
|
||||
result.add(byteString.asReadOnlyByteBuffer());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
left.writeTo(outputStream);
|
||||
right.writeTo(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
return new String(toByteArray(), charsetName);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// UTF-8 decoding
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
int leftPartial = left.partialIsValidUtf8(Utf8.COMPLETE, 0, leftLength);
|
||||
int state = right.partialIsValidUtf8(leftPartial, 0, right.size());
|
||||
return state == Utf8.COMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
int toIndex = offset + length;
|
||||
if (toIndex <= leftLength) {
|
||||
return left.partialIsValidUtf8(state, offset, length);
|
||||
} else if (offset >= leftLength) {
|
||||
return right.partialIsValidUtf8(state, offset - leftLength, length);
|
||||
} else {
|
||||
int leftLength = this.leftLength - offset;
|
||||
int leftPartial = left.partialIsValidUtf8(state, offset, leftLength);
|
||||
return right.partialIsValidUtf8(leftPartial, 0, length - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// equals() and hashCode()
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof ByteString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ByteString otherByteString = (ByteString) other;
|
||||
if (totalLength != otherByteString.size()) {
|
||||
return false;
|
||||
}
|
||||
if (totalLength == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// You don't really want to be calling equals on long strings, but since
|
||||
// we cache the hashCode, we effectively cache inequality. We use the cached
|
||||
// hashCode if it's already computed. It's arguable we should compute the
|
||||
// hashCode here, and if we're going to be testing a bunch of byteStrings,
|
||||
// it might even make sense.
|
||||
if (hash != 0) {
|
||||
int cachedOtherHash = otherByteString.peekCachedHashCode();
|
||||
if (cachedOtherHash != 0 && hash != cachedOtherHash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return equalsFragments(otherByteString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this string is equal to another of the same length by
|
||||
* iterating over the leaf nodes. On each step of the iteration, the
|
||||
* overlapping segments of the leaves are compared.
|
||||
*
|
||||
* @param other string of the same length as this one
|
||||
* @return true if the values of this string equals the value of the given
|
||||
* one
|
||||
*/
|
||||
private boolean equalsFragments(ByteString other) {
|
||||
int thisOffset = 0;
|
||||
Iterator<LiteralByteString> thisIter = new PieceIterator(this);
|
||||
LiteralByteString thisString = thisIter.next();
|
||||
|
||||
int thatOffset = 0;
|
||||
Iterator<LiteralByteString> thatIter = new PieceIterator(other);
|
||||
LiteralByteString thatString = thatIter.next();
|
||||
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
int thisRemaining = thisString.size() - thisOffset;
|
||||
int thatRemaining = thatString.size() - thatOffset;
|
||||
int bytesToCompare = Math.min(thisRemaining, thatRemaining);
|
||||
|
||||
// At least one of the offsets will be zero
|
||||
boolean stillEqual = (thisOffset == 0)
|
||||
? thisString.equalsRange(thatString, thatOffset, bytesToCompare)
|
||||
: thatString.equalsRange(thisString, thisOffset, bytesToCompare);
|
||||
if (!stillEqual) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos += bytesToCompare;
|
||||
if (pos >= totalLength) {
|
||||
if (pos == totalLength) {
|
||||
return true;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// We always get to the end of at least one of the pieces
|
||||
if (bytesToCompare == thisRemaining) { // If reached end of this
|
||||
thisOffset = 0;
|
||||
thisString = thisIter.next();
|
||||
} else {
|
||||
thisOffset += bytesToCompare;
|
||||
}
|
||||
if (bytesToCompare == thatRemaining) { // If reached end of that
|
||||
thatOffset = 0;
|
||||
thatString = thatIter.next();
|
||||
} else {
|
||||
thatOffset += bytesToCompare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which is safe
|
||||
* because of the Java Memory Model's "no out-of-thin-air values" guarantees
|
||||
* for ints.
|
||||
*/
|
||||
private int hash = 0;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = hash;
|
||||
|
||||
if (h == 0) {
|
||||
h = totalLength;
|
||||
h = partialHash(h, 0, totalLength);
|
||||
if (h == 0) {
|
||||
h = 1;
|
||||
}
|
||||
hash = h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int peekCachedHashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialHash(int h, int offset, int length) {
|
||||
int toIndex = offset + length;
|
||||
if (toIndex <= leftLength) {
|
||||
return left.partialHash(h, offset, length);
|
||||
} else if (offset >= leftLength) {
|
||||
return right.partialHash(h, offset - leftLength, length);
|
||||
} else {
|
||||
int leftLength = this.leftLength - offset;
|
||||
int leftPartial = left.partialHash(h, offset, leftLength);
|
||||
return right.partialHash(leftPartial, 0, length - leftLength);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Input stream
|
||||
|
||||
@Override
|
||||
public CodedInputStream newCodedInput() {
|
||||
return CodedInputStream.newInstance(new RopeInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInput() {
|
||||
return new RopeInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements the balancing algorithm of BAP95. In the paper the
|
||||
* authors use an array to keep track of pieces, while here we use a stack.
|
||||
* The tree is balanced by traversing subtrees in left to right order, and the
|
||||
* stack always contains the part of the string we've traversed so far.
|
||||
*
|
||||
* <p>One surprising aspect of the algorithm is the result of balancing is not
|
||||
* necessarily balanced, though it is nearly balanced. For details, see
|
||||
* BAP95.
|
||||
*/
|
||||
private static class Balancer {
|
||||
// Stack containing the part of the string, starting from the left, that
|
||||
// we've already traversed. The final string should be the equivalent of
|
||||
// concatenating the strings on the stack from bottom to top.
|
||||
private final Stack<ByteString> prefixesStack = new Stack<ByteString>();
|
||||
|
||||
private ByteString balance(ByteString left, ByteString right) {
|
||||
doBalance(left);
|
||||
doBalance(right);
|
||||
|
||||
// Sweep stack to gather the result
|
||||
ByteString partialString = prefixesStack.pop();
|
||||
while (!prefixesStack.isEmpty()) {
|
||||
ByteString newLeft = prefixesStack.pop();
|
||||
partialString = new RopeByteString(newLeft, partialString);
|
||||
}
|
||||
// We should end up with a RopeByteString since at a minimum we will
|
||||
// create one from concatenating left and right
|
||||
return partialString;
|
||||
}
|
||||
|
||||
private void doBalance(ByteString root) {
|
||||
// BAP95: Insert balanced subtrees whole. This means the result might not
|
||||
// be balanced, leading to repeated rebalancings on concatenate. However,
|
||||
// these rebalancings are shallow due to ignoring balanced subtrees, and
|
||||
// relatively few calls to insert() result.
|
||||
if (root.isBalanced()) {
|
||||
insert(root);
|
||||
} else if (root instanceof RopeByteString) {
|
||||
RopeByteString rbs = (RopeByteString) root;
|
||||
doBalance(rbs.left);
|
||||
doBalance(rbs.right);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Has a new type of ByteString been created? Found " +
|
||||
root.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a string on the balance stack (BAP95). BAP95 uses an array and
|
||||
* calls the elements in the array 'bins'. We instead use a stack, so the
|
||||
* 'bins' of lengths are represented by differences between the elements of
|
||||
* minLengthByDepth.
|
||||
*
|
||||
* <p>If the length bin for our string, and all shorter length bins, are
|
||||
* empty, we just push it on the stack. Otherwise, we need to start
|
||||
* concatenating, putting the given string in the "middle" and continuing
|
||||
* until we land in an empty length bin that matches the length of our
|
||||
* concatenation.
|
||||
*
|
||||
* @param byteString string to place on the balance stack
|
||||
*/
|
||||
private void insert(ByteString byteString) {
|
||||
int depthBin = getDepthBinForLength(byteString.size());
|
||||
int binEnd = minLengthByDepth[depthBin + 1];
|
||||
|
||||
// BAP95: Concatenate all trees occupying bins representing the length of
|
||||
// our new piece or of shorter pieces, to the extent that is possible.
|
||||
// The goal is to clear the bin which our piece belongs in, but that may
|
||||
// not be entirely possible if there aren't enough longer bins occupied.
|
||||
if (prefixesStack.isEmpty() || prefixesStack.peek().size() >= binEnd) {
|
||||
prefixesStack.push(byteString);
|
||||
} else {
|
||||
int binStart = minLengthByDepth[depthBin];
|
||||
|
||||
// Concatenate the subtrees of shorter length
|
||||
ByteString newTree = prefixesStack.pop();
|
||||
while (!prefixesStack.isEmpty()
|
||||
&& prefixesStack.peek().size() < binStart) {
|
||||
ByteString left = prefixesStack.pop();
|
||||
newTree = new RopeByteString(left, newTree);
|
||||
}
|
||||
|
||||
// Concatenate the given string
|
||||
newTree = new RopeByteString(newTree, byteString);
|
||||
|
||||
// Continue concatenating until we land in an empty bin
|
||||
while (!prefixesStack.isEmpty()) {
|
||||
depthBin = getDepthBinForLength(newTree.size());
|
||||
binEnd = minLengthByDepth[depthBin + 1];
|
||||
if (prefixesStack.peek().size() < binEnd) {
|
||||
ByteString left = prefixesStack.pop();
|
||||
newTree = new RopeByteString(left, newTree);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
prefixesStack.push(newTree);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDepthBinForLength(int length) {
|
||||
int depth = Arrays.binarySearch(minLengthByDepth, length);
|
||||
if (depth < 0) {
|
||||
// It wasn't an exact match, so convert to the index of the containing
|
||||
// fragment, which is one less even than the insertion point.
|
||||
int insertionPoint = -(depth + 1);
|
||||
depth = insertionPoint - 1;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a continuable tree traversal, which keeps the state
|
||||
* information which would exist on the stack in a recursive traversal instead
|
||||
* on a stack of "Bread Crumbs". The maximum depth of the stack in this
|
||||
* iterator is the same as the depth of the tree being traversed.
|
||||
*
|
||||
* <p>This iterator is used to implement
|
||||
* {@link RopeByteString#equalsFragments(ByteString)}.
|
||||
*/
|
||||
private static class PieceIterator implements Iterator<LiteralByteString> {
|
||||
|
||||
private final Stack<RopeByteString> breadCrumbs =
|
||||
new Stack<RopeByteString>();
|
||||
private LiteralByteString next;
|
||||
|
||||
private PieceIterator(ByteString root) {
|
||||
next = getLeafByLeft(root);
|
||||
}
|
||||
|
||||
private LiteralByteString getLeafByLeft(ByteString root) {
|
||||
ByteString pos = root;
|
||||
while (pos instanceof RopeByteString) {
|
||||
RopeByteString rbs = (RopeByteString) pos;
|
||||
breadCrumbs.push(rbs);
|
||||
pos = rbs.left;
|
||||
}
|
||||
return (LiteralByteString) pos;
|
||||
}
|
||||
|
||||
private LiteralByteString getNextNonEmptyLeaf() {
|
||||
while (true) {
|
||||
// Almost always, we go through this loop exactly once. However, if
|
||||
// we discover an empty string in the rope, we toss it and try again.
|
||||
if (breadCrumbs.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
LiteralByteString result = getLeafByLeft(breadCrumbs.pop().right);
|
||||
if (!result.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next item and advances one {@code LiteralByteString}.
|
||||
*
|
||||
* @return next non-empty LiteralByteString or {@code null}
|
||||
*/
|
||||
public LiteralByteString next() {
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
LiteralByteString result = next;
|
||||
next = getNextNonEmptyLeaf();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ByteIterator
|
||||
|
||||
@Override
|
||||
public ByteIterator iterator() {
|
||||
return new RopeByteIterator();
|
||||
}
|
||||
|
||||
private class RopeByteIterator implements ByteString.ByteIterator {
|
||||
|
||||
private final PieceIterator pieces;
|
||||
private ByteIterator bytes;
|
||||
int bytesRemaining;
|
||||
|
||||
private RopeByteIterator() {
|
||||
pieces = new PieceIterator(RopeByteString.this);
|
||||
bytes = pieces.next().iterator();
|
||||
bytesRemaining = size();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return (bytesRemaining > 0);
|
||||
}
|
||||
|
||||
public Byte next() {
|
||||
return nextByte(); // Does not instantiate a Byte
|
||||
}
|
||||
|
||||
public byte nextByte() {
|
||||
if (!bytes.hasNext()) {
|
||||
bytes = pieces.next().iterator();
|
||||
}
|
||||
--bytesRemaining;
|
||||
return bytes.nextByte();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is the {@link RopeByteString} equivalent for
|
||||
* {@link ByteArrayInputStream}.
|
||||
*/
|
||||
private class RopeInputStream extends InputStream {
|
||||
// Iterates through the pieces of the rope
|
||||
private PieceIterator pieceIterator;
|
||||
// The current piece
|
||||
private LiteralByteString currentPiece;
|
||||
// The size of the current piece
|
||||
private int currentPieceSize;
|
||||
// The index of the next byte to read in the current piece
|
||||
private int currentPieceIndex;
|
||||
// The offset of the start of the current piece in the rope byte string
|
||||
private int currentPieceOffsetInRope;
|
||||
// Offset in the buffer at which user called mark();
|
||||
private int mark;
|
||||
|
||||
public RopeInputStream() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[], int offset, int length) {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (offset < 0 || length < 0 || length > b.length - offset) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return readSkipInternal(b, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long length) {
|
||||
if (length < 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (length > Integer.MAX_VALUE) {
|
||||
length = Integer.MAX_VALUE;
|
||||
}
|
||||
return readSkipInternal(null, 0, (int) length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation of read and skip. If b != null, then read the
|
||||
* next {@code length} bytes into the buffer {@code b} at
|
||||
* offset {@code offset}. If b == null, then skip the next {@code length)
|
||||
* bytes.
|
||||
* <p>
|
||||
* This method assumes that all error checking has already happened.
|
||||
* <p>
|
||||
* Returns the actual number of bytes read or skipped.
|
||||
*/
|
||||
private int readSkipInternal(byte b[], int offset, int length) {
|
||||
int bytesRemaining = length;
|
||||
while (bytesRemaining > 0) {
|
||||
advanceIfCurrentPieceFullyRead();
|
||||
if (currentPiece == null) {
|
||||
if (bytesRemaining == length) {
|
||||
// We didn't manage to read anything
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Copy the bytes from this piece.
|
||||
int currentPieceRemaining = currentPieceSize - currentPieceIndex;
|
||||
int count = Math.min(currentPieceRemaining, bytesRemaining);
|
||||
if (b != null) {
|
||||
currentPiece.copyTo(b, currentPieceIndex, offset, count);
|
||||
offset += count;
|
||||
}
|
||||
currentPieceIndex += count;
|
||||
bytesRemaining -= count;
|
||||
}
|
||||
}
|
||||
// Return the number of bytes read.
|
||||
return length - bytesRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
advanceIfCurrentPieceFullyRead();
|
||||
if (currentPiece == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return currentPiece.byteAt(currentPieceIndex++) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
int bytesRead = currentPieceOffsetInRope + currentPieceIndex;
|
||||
return RopeByteString.this.size() - bytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readAheadLimit) {
|
||||
// Set the mark to our position in the byte string
|
||||
mark = currentPieceOffsetInRope + currentPieceIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
// Just reinitialize and skip the specified number of bytes.
|
||||
initialize();
|
||||
readSkipInternal(null, 0, mark);
|
||||
}
|
||||
|
||||
/** Common initialization code used by both the constructor and reset() */
|
||||
private void initialize() {
|
||||
pieceIterator = new PieceIterator(RopeByteString.this);
|
||||
currentPiece = pieceIterator.next();
|
||||
currentPieceSize = currentPiece.size();
|
||||
currentPieceIndex = 0;
|
||||
currentPieceOffsetInRope = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips to the next piece if we have read all the data in the current
|
||||
* piece. Sets currentPiece to null if we have reached the end of the
|
||||
* input.
|
||||
*/
|
||||
private void advanceIfCurrentPieceFullyRead() {
|
||||
if (currentPiece != null && currentPieceIndex == currentPieceSize) {
|
||||
// Generally, we can only go through this loop at most once, since
|
||||
// empty strings can't end up in a rope. But better to test.
|
||||
currentPieceOffsetInRope += currentPieceSize;
|
||||
currentPieceIndex = 0;
|
||||
if (pieceIterator.hasNext()) {
|
||||
currentPiece = pieceIterator.next();
|
||||
currentPieceSize = currentPiece.size();
|
||||
} else {
|
||||
currentPiece = null;
|
||||
currentPieceSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
java/src/main/java/com/google/protobuf/RpcCallback.java
Normal file
47
java/src/main/java/com/google/protobuf/RpcCallback.java
Normal file
@@ -0,0 +1,47 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Interface for an RPC callback, normally called when an RPC completes.
|
||||
* {@code ParameterType} is normally the method's response message type.
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcCallback<ParameterType> {
|
||||
void run(ParameterType parameter);
|
||||
}
|
||||
71
java/src/main/java/com/google/protobuf/RpcChannel.java
Normal file
71
java/src/main/java/com/google/protobuf/RpcChannel.java
Normal file
@@ -0,0 +1,71 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* <p>Abstract interface for an RPC channel. An {@code RpcChannel} represents a
|
||||
* communication line to a {@link Service} which can be used to call that
|
||||
* {@link Service}'s methods. The {@link Service} may be running on another
|
||||
* machine. Normally, you should not call an {@code RpcChannel} directly, but
|
||||
* instead construct a stub {@link Service} wrapping it. Example:
|
||||
*
|
||||
* <pre>
|
||||
* RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234");
|
||||
* RpcController controller = rpcImpl.newController();
|
||||
* MyService service = MyService.newStub(channel);
|
||||
* service.myMethod(controller, request, callback);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcChannel {
|
||||
/**
|
||||
* Call the given method of the remote service. This method is similar to
|
||||
* {@code Service.callMethod()} with one important difference: the caller
|
||||
* decides the types of the {@code Message} objects, not the callee. The
|
||||
* request may be of any type as long as
|
||||
* {@code request.getDescriptor() == method.getInputType()}.
|
||||
* The response passed to the callback will be of the same type as
|
||||
* {@code responsePrototype} (which must have
|
||||
* {@code getDescriptor() == method.getOutputType()}).
|
||||
*/
|
||||
void callMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
Message responsePrototype,
|
||||
RpcCallback<Message> done);
|
||||
}
|
||||
118
java/src/main/java/com/google/protobuf/RpcController.java
Normal file
118
java/src/main/java/com/google/protobuf/RpcController.java
Normal file
@@ -0,0 +1,118 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* <p>An {@code RpcController} mediates a single method call. The primary
|
||||
* purpose of the controller is to provide a way to manipulate settings
|
||||
* specific to the RPC implementation and to find out about RPC-level errors.
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* <p>The methods provided by the {@code RpcController} interface are intended
|
||||
* to be a "least common denominator" set of features which we expect all
|
||||
* implementations to support. Specific implementations may provide more
|
||||
* advanced features (e.g. deadline propagation).
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface RpcController {
|
||||
// -----------------------------------------------------------------
|
||||
// These calls may be made from the client side only. Their results
|
||||
// are undefined on the server side (may throw RuntimeExceptions).
|
||||
|
||||
/**
|
||||
* Resets the RpcController to its initial state so that it may be reused in
|
||||
* a new call. This can be called from the client side only. It must not
|
||||
* be called while an RPC is in progress.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* After a call has finished, returns true if the call failed. The possible
|
||||
* reasons for failure depend on the RPC implementation. {@code failed()}
|
||||
* most only be called on the client side, and must not be called before a
|
||||
* call has finished.
|
||||
*/
|
||||
boolean failed();
|
||||
|
||||
/**
|
||||
* If {@code failed()} is {@code true}, returns a human-readable description
|
||||
* of the error.
|
||||
*/
|
||||
String errorText();
|
||||
|
||||
/**
|
||||
* Advises the RPC system that the caller desires that the RPC call be
|
||||
* canceled. The RPC system may cancel it immediately, may wait awhile and
|
||||
* then cancel it, or may not even cancel the call at all. If the call is
|
||||
* canceled, the "done" callback will still be called and the RpcController
|
||||
* will indicate that the call failed at that time.
|
||||
*/
|
||||
void startCancel();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// These calls may be made from the server side only. Their results
|
||||
// are undefined on the client side (may throw RuntimeExceptions).
|
||||
|
||||
/**
|
||||
* Causes {@code failed()} to return true on the client side. {@code reason}
|
||||
* will be incorporated into the message returned by {@code errorText()}.
|
||||
* If you find you need to return machine-readable information about
|
||||
* failures, you should incorporate it into your response protocol buffer
|
||||
* and should NOT call {@code setFailed()}.
|
||||
*/
|
||||
void setFailed(String reason);
|
||||
|
||||
/**
|
||||
* If {@code true}, indicates that the client canceled the RPC, so the server
|
||||
* may as well give up on replying to it. This method must be called on the
|
||||
* server side only. The server should still call the final "done" callback.
|
||||
*/
|
||||
boolean isCanceled();
|
||||
|
||||
/**
|
||||
* Asks that the given callback be called when the RPC is canceled. The
|
||||
* parameter passed to the callback will always be {@code null}. The
|
||||
* callback will always be called exactly once. If the RPC completes without
|
||||
* being canceled, the callback will be called after completion. If the RPC
|
||||
* has already been canceled when NotifyOnCancel() is called, the callback
|
||||
* will be called immediately.
|
||||
*
|
||||
* <p>{@code notifyOnCancel()} must be called no more than once per request.
|
||||
* It must be called on the server side only.
|
||||
*/
|
||||
void notifyOnCancel(RpcCallback<Object> callback);
|
||||
}
|
||||
135
java/src/main/java/com/google/protobuf/RpcUtil.java
Normal file
135
java/src/main/java/com/google/protobuf/RpcUtil.java
Normal file
@@ -0,0 +1,135 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Grab-bag of utility functions useful when dealing with RPCs.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class RpcUtil {
|
||||
private RpcUtil() {}
|
||||
|
||||
/**
|
||||
* Take an {@code RpcCallback<Message>} and convert it to an
|
||||
* {@code RpcCallback} accepting a specific message type. This is always
|
||||
* type-safe (parameter type contravariance).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <Type extends Message> RpcCallback<Type>
|
||||
specializeCallback(final RpcCallback<Message> originalCallback) {
|
||||
return (RpcCallback<Type>)originalCallback;
|
||||
// The above cast works, but only due to technical details of the Java
|
||||
// implementation. A more theoretically correct -- but less efficient --
|
||||
// implementation would be as follows:
|
||||
// return new RpcCallback<Type>() {
|
||||
// public void run(Type parameter) {
|
||||
// originalCallback.run(parameter);
|
||||
// }
|
||||
// };
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an {@code RpcCallback} accepting a specific message type and convert
|
||||
* it to an {@code RpcCallback<Message>}. The generalized callback will
|
||||
* accept any message object which has the same descriptor, and will convert
|
||||
* it to the correct class before calling the original callback. However,
|
||||
* if the generalized callback is given a message with a different descriptor,
|
||||
* an exception will be thrown.
|
||||
*/
|
||||
public static <Type extends Message>
|
||||
RpcCallback<Message> generalizeCallback(
|
||||
final RpcCallback<Type> originalCallback,
|
||||
final Class<Type> originalClass,
|
||||
final Type defaultInstance) {
|
||||
return new RpcCallback<Message>() {
|
||||
public void run(final Message parameter) {
|
||||
Type typedParameter;
|
||||
try {
|
||||
typedParameter = originalClass.cast(parameter);
|
||||
} catch (ClassCastException ignored) {
|
||||
typedParameter = copyAsType(defaultInstance, parameter);
|
||||
}
|
||||
originalCallback.run(typedParameter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message of type "Type" which is a copy of "source". "source"
|
||||
* must have the same descriptor but may be a different class (e.g.
|
||||
* DynamicMessage).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <Type extends Message> Type copyAsType(
|
||||
final Type typeDefaultInstance, final Message source) {
|
||||
return (Type)typeDefaultInstance.newBuilderForType()
|
||||
.mergeFrom(source)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a callback which can only be called once. This may be useful for
|
||||
* security, when passing a callback to untrusted code: most callbacks do
|
||||
* not expect to be called more than once, so doing so may expose bugs if it
|
||||
* is not prevented.
|
||||
*/
|
||||
public static <ParameterType>
|
||||
RpcCallback<ParameterType> newOneTimeCallback(
|
||||
final RpcCallback<ParameterType> originalCallback) {
|
||||
return new RpcCallback<ParameterType>() {
|
||||
private boolean alreadyCalled = false;
|
||||
|
||||
public void run(final ParameterType parameter) {
|
||||
synchronized(this) {
|
||||
if (alreadyCalled) {
|
||||
throw new AlreadyCalledException();
|
||||
}
|
||||
alreadyCalled = true;
|
||||
}
|
||||
|
||||
originalCallback.run(parameter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when a one-time callback is called more than once.
|
||||
*/
|
||||
public static final class AlreadyCalledException extends RuntimeException {
|
||||
private static final long serialVersionUID = 5469741279507848266L;
|
||||
|
||||
public AlreadyCalledException() {
|
||||
super("This RpcCallback was already called and cannot be called " +
|
||||
"multiple times.");
|
||||
}
|
||||
}
|
||||
}
|
||||
117
java/src/main/java/com/google/protobuf/Service.java
Normal file
117
java/src/main/java/com/google/protobuf/Service.java
Normal file
@@ -0,0 +1,117 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Abstract base interface for protocol-buffer-based RPC services. Services
|
||||
* themselves are abstract classes (implemented either by servers or as
|
||||
* stubs), but they subclass this base interface. The methods of this
|
||||
* interface can be used to call the methods of the service without knowing
|
||||
* its exact type at compile time (analogous to the Message interface).
|
||||
*
|
||||
* <p>Starting with version 2.3.0, RPC implementations should not try to build
|
||||
* on this, but should instead provide code generator plugins which generate
|
||||
* code specific to the particular RPC implementation. This way the generated
|
||||
* code can be more appropriate for the implementation in use and can avoid
|
||||
* unnecessary layers of indirection.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public interface Service {
|
||||
/**
|
||||
* Get the {@code ServiceDescriptor} describing this service and its methods.
|
||||
*/
|
||||
Descriptors.ServiceDescriptor getDescriptorForType();
|
||||
|
||||
/**
|
||||
* <p>Call a method of the service specified by MethodDescriptor. This is
|
||||
* normally implemented as a simple {@code switch()} that calls the standard
|
||||
* definitions of the service's methods.
|
||||
*
|
||||
* <p>Preconditions:
|
||||
* <ul>
|
||||
* <li>{@code method.getService() == getDescriptorForType()}
|
||||
* <li>{@code request} is of the exact same class as the object returned by
|
||||
* {@code getRequestPrototype(method)}.
|
||||
* <li>{@code controller} is of the correct type for the RPC implementation
|
||||
* being used by this Service. For stubs, the "correct type" depends
|
||||
* on the RpcChannel which the stub is using. Server-side Service
|
||||
* implementations are expected to accept whatever type of
|
||||
* {@code RpcController} the server-side RPC implementation uses.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Postconditions:
|
||||
* <ul>
|
||||
* <li>{@code done} will be called when the method is complete. This may be
|
||||
* before {@code callMethod()} returns or it may be at some point in
|
||||
* the future.
|
||||
* <li>The parameter to {@code done} is the response. It must be of the
|
||||
* exact same type as would be returned by
|
||||
* {@code getResponsePrototype(method)}.
|
||||
* <li>If the RPC failed, the parameter to {@code done} will be
|
||||
* {@code null}. Further details about the failure can be found by
|
||||
* querying {@code controller}.
|
||||
* </ul>
|
||||
*/
|
||||
void callMethod(Descriptors.MethodDescriptor method,
|
||||
RpcController controller,
|
||||
Message request,
|
||||
RpcCallback<Message> done);
|
||||
|
||||
/**
|
||||
* <p>{@code callMethod()} requires that the request passed in is of a
|
||||
* particular subclass of {@code Message}. {@code getRequestPrototype()}
|
||||
* gets the default instances of this type for a given method. You can then
|
||||
* call {@code Message.newBuilderForType()} on this instance to
|
||||
* construct a builder to build an object which you can then pass to
|
||||
* {@code callMethod()}.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>
|
||||
* MethodDescriptor method =
|
||||
* service.getDescriptorForType().findMethodByName("Foo");
|
||||
* Message request =
|
||||
* stub.getRequestPrototype(method).newBuilderForType()
|
||||
* .mergeFrom(input).build();
|
||||
* service.callMethod(method, request, callback);
|
||||
* </pre>
|
||||
*/
|
||||
Message getRequestPrototype(Descriptors.MethodDescriptor method);
|
||||
|
||||
/**
|
||||
* Like {@code getRequestPrototype()}, but gets a prototype of the response
|
||||
* message. {@code getResponsePrototype()} is generally not needed because
|
||||
* the {@code Service} implementation constructs the response message itself,
|
||||
* but it may be useful in some cases to know ahead of time what type of
|
||||
* object will be returned.
|
||||
*/
|
||||
Message getResponsePrototype(Descriptors.MethodDescriptor method);
|
||||
}
|
||||
52
java/src/main/java/com/google/protobuf/ServiceException.java
Normal file
52
java/src/main/java/com/google/protobuf/ServiceException.java
Normal file
@@ -0,0 +1,52 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Thrown by blocking RPC methods when a failure occurs.
|
||||
*
|
||||
* @author cpovirk@google.com (Chris Povirk)
|
||||
*/
|
||||
public class ServiceException extends Exception {
|
||||
private static final long serialVersionUID = -1219262335729891920L;
|
||||
|
||||
public ServiceException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServiceException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ServiceException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
241
java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
Normal file
241
java/src/main/java/com/google/protobuf/SingleFieldBuilder.java
Normal file
@@ -0,0 +1,241 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* {@code SingleFieldBuilder} implements a structure that a protocol
|
||||
* message uses to hold a single field of another protocol message. It supports
|
||||
* the classical use case of setting an immutable {@link Message} as the value
|
||||
* of the field and is highly optimized around this.
|
||||
* <br>
|
||||
* It also supports the additional use case of setting a {@link Message.Builder}
|
||||
* as the field and deferring conversion of that {@code Builder}
|
||||
* to an immutable {@code Message}. In this way, it's possible to maintain
|
||||
* a tree of {@code Builder}'s that acts as a fully read/write data
|
||||
* structure.
|
||||
* <br>
|
||||
* Logically, one can think of a tree of builders as converting the entire tree
|
||||
* to messages when build is called on the root or when any method is called
|
||||
* that desires a Message instead of a Builder. In terms of the implementation,
|
||||
* the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
|
||||
* classes cache messages that were created so that messages only need to be
|
||||
* created when some change occured in its builder or a builder for one of its
|
||||
* descendants.
|
||||
*
|
||||
* @param <MType> the type of message for the field
|
||||
* @param <BType> the type of builder for the field
|
||||
* @param <IType> the common interface for the message and the builder
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class SingleFieldBuilder
|
||||
<MType extends GeneratedMessage,
|
||||
BType extends GeneratedMessage.Builder,
|
||||
IType extends MessageOrBuilder>
|
||||
implements GeneratedMessage.BuilderParent {
|
||||
|
||||
// Parent to send changes to.
|
||||
private GeneratedMessage.BuilderParent parent;
|
||||
|
||||
// Invariant: one of builder or message fields must be non-null.
|
||||
|
||||
// If set, this is the case where we are backed by a builder. In this case,
|
||||
// message field represents a cached message for the builder (or null if
|
||||
// there is no cached message).
|
||||
private BType builder;
|
||||
|
||||
// If builder is non-null, this represents a cached message from the builder.
|
||||
// If builder is null, this is the authoritative message for the field.
|
||||
private MType message;
|
||||
|
||||
// Indicates that we've built a message and so we are now obligated
|
||||
// to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
|
||||
private boolean isClean;
|
||||
|
||||
public SingleFieldBuilder(
|
||||
MType message,
|
||||
GeneratedMessage.BuilderParent parent,
|
||||
boolean isClean) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.message = message;
|
||||
this.parent = parent;
|
||||
this.isClean = isClean;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// Null out parent so we stop sending it invalidations.
|
||||
parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message for the field. If the message is currently stored
|
||||
* as a {@code Builder}, it is converted to a {@code Message} by
|
||||
* calling {@link Message.Builder#buildPartial} on it. If no message has
|
||||
* been set, returns the default instance of the message.
|
||||
*
|
||||
* @return the message for the field
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public MType getMessage() {
|
||||
if (message == null) {
|
||||
// If message is null, the invariant is that we must be have a builder.
|
||||
message = (MType) builder.buildPartial();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the message and returns it.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public MType build() {
|
||||
// Now that build has been called, we are required to dispatch
|
||||
// invalidations.
|
||||
isClean = true;
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a builder for the field. If no builder has been created yet, a
|
||||
* builder is created on demand by calling {@link Message#toBuilder}.
|
||||
*
|
||||
* @return The builder for the field
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public BType getBuilder() {
|
||||
if (builder == null) {
|
||||
// builder.mergeFrom() on a fresh builder
|
||||
// does not create any sub-objects with independent clean/dirty states,
|
||||
// therefore setting the builder itself to clean without actually calling
|
||||
// build() cannot break any invariants.
|
||||
builder = (BType) message.newBuilderForType(this);
|
||||
builder.mergeFrom(message); // no-op if message is the default message
|
||||
builder.markClean();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base class interface for the field. This may either be a builder
|
||||
* or a message. It will return whatever is more efficient.
|
||||
*
|
||||
* @return the message or builder for the field as the base class interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public IType getMessageOrBuilder() {
|
||||
if (builder != null) {
|
||||
return (IType) builder;
|
||||
} else {
|
||||
return (IType) message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a message for the field replacing any existing value.
|
||||
*
|
||||
* @param message the message to set
|
||||
* @return the builder
|
||||
*/
|
||||
public SingleFieldBuilder<MType, BType, IType> setMessage(
|
||||
MType message) {
|
||||
if (message == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.message = message;
|
||||
if (builder != null) {
|
||||
builder.dispose();
|
||||
builder = null;
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the field from another field.
|
||||
*
|
||||
* @param value the value to merge from
|
||||
* @return the builder
|
||||
*/
|
||||
public SingleFieldBuilder<MType, BType, IType> mergeFrom(
|
||||
MType value) {
|
||||
if (builder == null && message == message.getDefaultInstanceForType()) {
|
||||
message = value;
|
||||
} else {
|
||||
getBuilder().mergeFrom(value);
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the value of the field.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SingleFieldBuilder<MType, BType, IType> clear() {
|
||||
message = (MType) (message != null ?
|
||||
message.getDefaultInstanceForType() :
|
||||
builder.getDefaultInstanceForType());
|
||||
if (builder != null) {
|
||||
builder.dispose();
|
||||
builder = null;
|
||||
}
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a the builder or one of its nested children has changed
|
||||
* and any parent should be notified of its invalidation.
|
||||
*/
|
||||
private void onChanged() {
|
||||
// If builder is null, this is the case where onChanged is being called
|
||||
// from setMessage or clear.
|
||||
if (builder != null) {
|
||||
message = null;
|
||||
}
|
||||
if (isClean && parent != null) {
|
||||
parent.markDirty();
|
||||
|
||||
// Don't keep dispatching invalidations until build is called again.
|
||||
isClean = false;
|
||||
}
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void markDirty() {
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
618
java/src/main/java/com/google/protobuf/SmallSortedMap.java
Normal file
618
java/src/main/java/com/google/protobuf/SmallSortedMap.java
Normal file
@@ -0,0 +1,618 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
|
||||
/**
|
||||
* A custom map implementation from FieldDescriptor to Object optimized to
|
||||
* minimize the number of memory allocations for instances with a small number
|
||||
* of mappings. The implementation stores the first {@code k} mappings in an
|
||||
* array for a configurable value of {@code k}, allowing direct access to the
|
||||
* corresponding {@code Entry}s without the need to create an Iterator. The
|
||||
* remaining entries are stored in an overflow map. Iteration over the entries
|
||||
* in the map should be done as follows:
|
||||
*
|
||||
* <pre> {@code
|
||||
* for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) {
|
||||
* process(fieldMap.getArrayEntryAt(i));
|
||||
* }
|
||||
* for (Map.Entry<K, V> entry : fieldMap.getOverflowEntries()) {
|
||||
* process(entry);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* The resulting iteration is in order of ascending field tag number. The
|
||||
* object returned by {@link #entrySet()} adheres to the same contract but is
|
||||
* less efficient as it necessarily involves creating an object for iteration.
|
||||
* <p>
|
||||
* The tradeoff for this memory efficiency is that the worst case running time
|
||||
* of the {@code put()} operation is {@code O(k + lg n)}, which happens when
|
||||
* entries are added in descending order. {@code k} should be chosen such that
|
||||
* it covers enough common cases without adversely affecting larger maps. In
|
||||
* practice, the worst case scenario does not happen for extensions because
|
||||
* extension fields are serialized and deserialized in order of ascending tag
|
||||
* number, but the worst case scenario can happen for DynamicMessages.
|
||||
* <p>
|
||||
* The running time for all other operations is similar to that of
|
||||
* {@code TreeMap}.
|
||||
* <p>
|
||||
* Instances are not thread-safe until {@link #makeImmutable()} is called,
|
||||
* after which any modifying operation will result in an
|
||||
* {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author darick@google.com Darick Tong
|
||||
*/
|
||||
// This class is final for all intents and purposes because the constructor is
|
||||
// private. However, the FieldDescriptor-specific logic is encapsulated in
|
||||
// a subclass to aid testability of the core logic.
|
||||
class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
|
||||
|
||||
/**
|
||||
* Creates a new instance for mapping FieldDescriptors to their values.
|
||||
* The {@link #makeImmutable()} implementation will convert the List values
|
||||
* of any repeated fields to unmodifiable lists.
|
||||
*
|
||||
* @param arraySize The size of the entry array containing the
|
||||
* lexicographically smallest mappings.
|
||||
*/
|
||||
static <FieldDescriptorType extends
|
||||
FieldSet.FieldDescriptorLite<FieldDescriptorType>>
|
||||
SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) {
|
||||
return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void makeImmutable() {
|
||||
if (!isImmutable()) {
|
||||
for (int i = 0; i < getNumArrayEntries(); i++) {
|
||||
final Map.Entry<FieldDescriptorType, Object> entry =
|
||||
getArrayEntryAt(i);
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List) entry.getValue();
|
||||
entry.setValue(Collections.unmodifiableList(value));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<FieldDescriptorType, Object> entry :
|
||||
getOverflowEntries()) {
|
||||
if (entry.getKey().isRepeated()) {
|
||||
final List value = (List) entry.getValue();
|
||||
entry.setValue(Collections.unmodifiableList(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
super.makeImmutable();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for testing.
|
||||
*
|
||||
* @param arraySize The size of the entry array containing the
|
||||
* lexicographically smallest mappings.
|
||||
*/
|
||||
static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest(
|
||||
int arraySize) {
|
||||
return new SmallSortedMap<K, V>(arraySize);
|
||||
}
|
||||
|
||||
private final int maxArraySize;
|
||||
// The "entry array" is actually a List because generic arrays are not
|
||||
// allowed. ArrayList also nicely handles the entry shifting on inserts and
|
||||
// removes.
|
||||
private List<Entry> entryList;
|
||||
private Map<K, V> overflowEntries;
|
||||
private boolean isImmutable;
|
||||
// The EntrySet is a stateless view of the Map. It's initialized the first
|
||||
// time it is requested and reused henceforth.
|
||||
private volatile EntrySet lazyEntrySet;
|
||||
|
||||
/**
|
||||
* @code arraySize Size of the array in which the lexicographically smallest
|
||||
* mappings are stored. (i.e. the {@code k} referred to in the class
|
||||
* documentation).
|
||||
*/
|
||||
private SmallSortedMap(int arraySize) {
|
||||
this.maxArraySize = arraySize;
|
||||
this.entryList = Collections.emptyList();
|
||||
this.overflowEntries = Collections.emptyMap();
|
||||
}
|
||||
|
||||
/** Make this map immutable from this point forward. */
|
||||
public void makeImmutable() {
|
||||
if (!isImmutable) {
|
||||
// Note: There's no need to wrap the entryList in an unmodifiableList
|
||||
// because none of the list's accessors are exposed. The iterator() of
|
||||
// overflowEntries, on the other hand, is exposed so it must be made
|
||||
// unmodifiable.
|
||||
overflowEntries = overflowEntries.isEmpty() ?
|
||||
Collections.<K, V>emptyMap() :
|
||||
Collections.unmodifiableMap(overflowEntries);
|
||||
isImmutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Whether {@link #makeImmutable()} has been called. */
|
||||
public boolean isImmutable() {
|
||||
return isImmutable;
|
||||
}
|
||||
|
||||
/** @return The number of entries in the entry array. */
|
||||
public int getNumArrayEntries() {
|
||||
return entryList.size();
|
||||
}
|
||||
|
||||
/** @return The array entry at the given {@code index}. */
|
||||
public Map.Entry<K, V> getArrayEntryAt(int index) {
|
||||
return entryList.get(index);
|
||||
}
|
||||
|
||||
/** @return There number of overflow entries. */
|
||||
public int getNumOverflowEntries() {
|
||||
return overflowEntries.size();
|
||||
}
|
||||
|
||||
/** @return An iterable over the overflow entries. */
|
||||
public Iterable<Map.Entry<K, V>> getOverflowEntries() {
|
||||
return overflowEntries.isEmpty() ?
|
||||
EmptySet.<Map.Entry<K, V>>iterable() :
|
||||
overflowEntries.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return entryList.size() + overflowEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V get(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
return entryList.get(index).getValue();
|
||||
}
|
||||
return overflowEntries.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
checkMutable();
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
// Replace existing array entry.
|
||||
return entryList.get(index).setValue(value);
|
||||
}
|
||||
ensureEntryArrayMutable();
|
||||
final int insertionPoint = -(index + 1);
|
||||
if (insertionPoint >= maxArraySize) {
|
||||
// Put directly in overflow.
|
||||
return getOverflowEntriesMutable().put(key, value);
|
||||
}
|
||||
// Insert new Entry in array.
|
||||
if (entryList.size() == maxArraySize) {
|
||||
// Shift the last array entry into overflow.
|
||||
final Entry lastEntryInArray = entryList.remove(maxArraySize - 1);
|
||||
getOverflowEntriesMutable().put(lastEntryInArray.getKey(),
|
||||
lastEntryInArray.getValue());
|
||||
}
|
||||
entryList.add(insertionPoint, new Entry(key, value));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
checkMutable();
|
||||
if (!entryList.isEmpty()) {
|
||||
entryList.clear();
|
||||
}
|
||||
if (!overflowEntries.isEmpty()) {
|
||||
overflowEntries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation throws a {@code ClassCastException} if o is not an
|
||||
* object of type {@code K}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public V remove(Object o) {
|
||||
checkMutable();
|
||||
@SuppressWarnings("unchecked")
|
||||
final K key = (K) o;
|
||||
final int index = binarySearchInArray(key);
|
||||
if (index >= 0) {
|
||||
return removeArrayEntryAt(index);
|
||||
}
|
||||
// overflowEntries might be Collections.unmodifiableMap(), so only
|
||||
// call remove() if it is non-empty.
|
||||
if (overflowEntries.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return overflowEntries.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private V removeArrayEntryAt(int index) {
|
||||
checkMutable();
|
||||
final V removed = entryList.remove(index).getValue();
|
||||
if (!overflowEntries.isEmpty()) {
|
||||
// Shift the first entry in the overflow to be the last entry in the
|
||||
// array.
|
||||
final Iterator<Map.Entry<K, V>> iterator =
|
||||
getOverflowEntriesMutable().entrySet().iterator();
|
||||
entryList.add(new Entry(iterator.next()));
|
||||
iterator.remove();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key The key to find in the entry array.
|
||||
* @return The returned integer position follows the same semantics as the
|
||||
* value returned by {@link java.util.Arrays#binarySearch()}.
|
||||
*/
|
||||
private int binarySearchInArray(K key) {
|
||||
int left = 0;
|
||||
int right = entryList.size() - 1;
|
||||
|
||||
// Optimization: For the common case in which entries are added in
|
||||
// ascending tag order, check the largest element in the array before
|
||||
// doing a full binary search.
|
||||
if (right >= 0) {
|
||||
int cmp = key.compareTo(entryList.get(right).getKey());
|
||||
if (cmp > 0) {
|
||||
return -(right + 2); // Insert point is after "right".
|
||||
} else if (cmp == 0) {
|
||||
return right;
|
||||
}
|
||||
}
|
||||
|
||||
while (left <= right) {
|
||||
int mid = (left + right) / 2;
|
||||
int cmp = key.compareTo(entryList.get(mid).getKey());
|
||||
if (cmp < 0) {
|
||||
right = mid - 1;
|
||||
} else if (cmp > 0) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return -(left + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the AbstractMap implementation of {@code keySet()} and
|
||||
* {@code values()}, the entry set is created the first time this method is
|
||||
* called, and returned in response to all subsequent calls.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
if (lazyEntrySet == null) {
|
||||
lazyEntrySet = new EntrySet();
|
||||
}
|
||||
return lazyEntrySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException if {@link #makeImmutable()} has
|
||||
* has been called.
|
||||
*/
|
||||
private void checkMutable() {
|
||||
if (isImmutable) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@link SortedMap} to which overflow entries mappings can be
|
||||
* added or removed.
|
||||
* @throws UnsupportedOperationException if {@link #makeImmutable()} has been
|
||||
* called.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private SortedMap<K, V> getOverflowEntriesMutable() {
|
||||
checkMutable();
|
||||
if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) {
|
||||
overflowEntries = new TreeMap<K, V>();
|
||||
}
|
||||
return (SortedMap<K, V>) overflowEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily creates the entry list. Any code that adds to the list must first
|
||||
* call this method.
|
||||
*/
|
||||
private void ensureEntryArrayMutable() {
|
||||
checkMutable();
|
||||
if (entryList.isEmpty() && !(entryList instanceof ArrayList)) {
|
||||
entryList = new ArrayList<Entry>(maxArraySize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry implementation that implements Comparable in order to support
|
||||
* binary search within the entry array. Also checks mutability in
|
||||
* {@link #setValue()}.
|
||||
*/
|
||||
private class Entry implements Map.Entry<K, V>, Comparable<Entry> {
|
||||
|
||||
private final K key;
|
||||
private V value;
|
||||
|
||||
Entry(Map.Entry<K, V> copy) {
|
||||
this(copy.getKey(), copy.getValue());
|
||||
}
|
||||
|
||||
Entry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int compareTo(Entry other) {
|
||||
return getKey().compareTo(other.getKey());
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public V setValue(V newValue) {
|
||||
checkMutable();
|
||||
final V oldValue = this.value;
|
||||
this.value = newValue;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
|
||||
return equals(key, other.getKey()) && equals(value, other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
/** equals() that handles null values. */
|
||||
private boolean equals(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stateless view of the entries in the field map.
|
||||
*/
|
||||
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new EntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return SmallSortedMap.this.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link ClassCastException} if o is not of the expected type.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
|
||||
final V existing = get(entry.getKey());
|
||||
final V value = entry.getValue();
|
||||
return existing == value ||
|
||||
(existing != null && existing.equals(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<K, V> entry) {
|
||||
if (!contains(entry)) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link ClassCastException} if o is not of the expected type.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
|
||||
if (contains(entry)) {
|
||||
SmallSortedMap.this.remove(entry.getKey());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
SmallSortedMap.this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator implementation that switches from the entry array to the overflow
|
||||
* entries appropriately.
|
||||
*/
|
||||
private class EntryIterator implements Iterator<Map.Entry<K, V>> {
|
||||
|
||||
private int pos = -1;
|
||||
private boolean nextCalledBeforeRemove;
|
||||
private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return (pos + 1) < entryList.size() ||
|
||||
getOverflowIterator().hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Map.Entry<K, V> next() {
|
||||
nextCalledBeforeRemove = true;
|
||||
// Always increment pos so that we know whether the last returned value
|
||||
// was from the array or from overflow.
|
||||
if (++pos < entryList.size()) {
|
||||
return entryList.get(pos);
|
||||
}
|
||||
return getOverflowIterator().next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
if (!nextCalledBeforeRemove) {
|
||||
throw new IllegalStateException("remove() was called before next()");
|
||||
}
|
||||
nextCalledBeforeRemove = false;
|
||||
checkMutable();
|
||||
|
||||
if (pos < entryList.size()) {
|
||||
removeArrayEntryAt(pos--);
|
||||
} else {
|
||||
getOverflowIterator().remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It is important to create the overflow iterator only after the array
|
||||
* entries have been iterated over because the overflow entry set changes
|
||||
* when the client calls remove() on the array entries, which invalidates
|
||||
* any existing iterators.
|
||||
*/
|
||||
private Iterator<Map.Entry<K, V>> getOverflowIterator() {
|
||||
if (lazyOverflowIterator == null) {
|
||||
lazyOverflowIterator = overflowEntries.entrySet().iterator();
|
||||
}
|
||||
return lazyOverflowIterator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that holds immutable instances of an Iterable/Iterator that
|
||||
* we return when the overflow entries is empty. This eliminates the creation
|
||||
* of an Iterator object when there is nothing to iterate over.
|
||||
*/
|
||||
private static class EmptySet {
|
||||
|
||||
private static final Iterator<Object> ITERATOR = new Iterator<Object>() {
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Object next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
private static final Iterable<Object> ITERABLE = new Iterable<Object>() {
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public Iterator<Object> iterator() {
|
||||
return ITERATOR;
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Iterable<T> iterable() {
|
||||
return (Iterable<T>) ITERABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
1559
java/src/main/java/com/google/protobuf/TextFormat.java
Normal file
1559
java/src/main/java/com/google/protobuf/TextFormat.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Thrown when attempting to build a protocol message that is missing required
|
||||
* fields. This is a {@code RuntimeException} because it normally represents
|
||||
* a programming error: it happens when some code which constructs a message
|
||||
* fails to set all the fields. {@code parseFrom()} methods <b>do not</b>
|
||||
* throw this; they throw an {@link InvalidProtocolBufferException} if
|
||||
* required fields are missing, because it is not a programming error to
|
||||
* receive an incomplete message. In other words,
|
||||
* {@code UninitializedMessageException} should never be thrown by correct
|
||||
* code, but {@code InvalidProtocolBufferException} might be.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class UninitializedMessageException extends RuntimeException {
|
||||
private static final long serialVersionUID = -7466929953374883507L;
|
||||
|
||||
public UninitializedMessageException(final MessageLite message) {
|
||||
super("Message was missing required fields. (Lite runtime could not " +
|
||||
"determine which fields were missing).");
|
||||
missingFields = null;
|
||||
}
|
||||
|
||||
public UninitializedMessageException(final List<String> missingFields) {
|
||||
super(buildDescription(missingFields));
|
||||
this.missingFields = missingFields;
|
||||
}
|
||||
|
||||
private final List<String> missingFields;
|
||||
|
||||
/**
|
||||
* Get a list of human-readable names of required fields missing from this
|
||||
* message. Each name is a full path to a field, e.g. "foo.bar[5].baz".
|
||||
* Returns null if the lite runtime was used, since it lacks the ability to
|
||||
* find missing fields.
|
||||
*/
|
||||
public List<String> getMissingFields() {
|
||||
return Collections.unmodifiableList(missingFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this exception to an {@link InvalidProtocolBufferException}.
|
||||
* When a parsed message is missing required fields, this should be thrown
|
||||
* instead of {@code UninitializedMessageException}.
|
||||
*/
|
||||
public InvalidProtocolBufferException asInvalidProtocolBufferException() {
|
||||
return new InvalidProtocolBufferException(getMessage());
|
||||
}
|
||||
|
||||
/** Construct the description string for this exception. */
|
||||
private static String buildDescription(final List<String> missingFields) {
|
||||
final StringBuilder description =
|
||||
new StringBuilder("Message missing required fields: ");
|
||||
boolean first = true;
|
||||
for (final String field : missingFields) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
description.append(", ");
|
||||
}
|
||||
description.append(field);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
}
|
||||
978
java/src/main/java/com/google/protobuf/UnknownFieldSet.java
Normal file
978
java/src/main/java/com/google/protobuf/UnknownFieldSet.java
Normal file
@@ -0,0 +1,978 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* {@code UnknownFieldSet} is used to keep track of fields which were seen when
|
||||
* parsing a protocol message but whose field numbers or types are unrecognized.
|
||||
* This most frequently occurs when new fields are added to a message type
|
||||
* and then messages containing those fields are read by old software that was
|
||||
* compiled before the new types were added.
|
||||
*
|
||||
* <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every
|
||||
* {@link Message.Builder} contains an {@link Builder}).
|
||||
*
|
||||
* <p>Most users will never need to use this class.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class UnknownFieldSet implements MessageLite {
|
||||
private UnknownFieldSet() {}
|
||||
|
||||
/** Create a new {@link Builder}. */
|
||||
public static Builder newBuilder() {
|
||||
return Builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Builder} and initialize it to be a copy
|
||||
* of {@code copyFrom}.
|
||||
*/
|
||||
public static Builder newBuilder(final UnknownFieldSet copyFrom) {
|
||||
return newBuilder().mergeFrom(copyFrom);
|
||||
}
|
||||
|
||||
/** Get an empty {@code UnknownFieldSet}. */
|
||||
public static UnknownFieldSet getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
public UnknownFieldSet getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
private static final UnknownFieldSet defaultInstance =
|
||||
new UnknownFieldSet(Collections.<Integer, Field>emptyMap());
|
||||
|
||||
/**
|
||||
* Construct an {@code UnknownFieldSet} around the given map. The map is
|
||||
* expected to be immutable.
|
||||
*/
|
||||
private UnknownFieldSet(final Map<Integer, Field> fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
private Map<Integer, Field> fields;
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
return (other instanceof UnknownFieldSet) &&
|
||||
fields.equals(((UnknownFieldSet) other).fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return fields.hashCode();
|
||||
}
|
||||
|
||||
/** Get a map of fields in the set by number. */
|
||||
public Map<Integer, Field> asMap() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** Check if the given field number is present in the set. */
|
||||
public boolean hasField(final int number) {
|
||||
return fields.containsKey(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field by number. Returns an empty field if not present. Never
|
||||
* returns {@code null}.
|
||||
*/
|
||||
public Field getField(final int number) {
|
||||
final Field result = fields.get(number);
|
||||
return (result == null) ? Field.getDefaultInstance() : result;
|
||||
}
|
||||
|
||||
/** Serializes the set and writes it to {@code output}. */
|
||||
public void writeTo(final CodedOutputStream output) throws IOException {
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
entry.getValue().writeTo(entry.getKey(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the set to a string in protocol buffer text format. This is
|
||||
* just a trivial wrapper around
|
||||
* {@link TextFormat#printToString(UnknownFieldSet)}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return TextFormat.printToString(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code ByteString} and returns it. This is
|
||||
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public ByteString toByteString() {
|
||||
try {
|
||||
final ByteString.CodedBuilder out =
|
||||
ByteString.newCodedBuilder(getSerializedSize());
|
||||
writeTo(out.getCodedOutput());
|
||||
return out.build();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message to a {@code byte} array and returns it. This is
|
||||
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
try {
|
||||
final byte[] result = new byte[getSerializedSize()];
|
||||
final CodedOutputStream output = CodedOutputStream.newInstance(result);
|
||||
writeTo(output);
|
||||
output.checkNoSpaceLeft();
|
||||
return result;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Serializing to a byte array threw an IOException " +
|
||||
"(should never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message and writes it to {@code output}. This is just a
|
||||
* trivial wrapper around {@link #writeTo(CodedOutputStream)}.
|
||||
*/
|
||||
public void writeTo(final OutputStream output) throws IOException {
|
||||
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
public void writeDelimitedTo(OutputStream output) throws IOException {
|
||||
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
|
||||
codedOutput.writeRawVarint32(getSerializedSize());
|
||||
writeTo(codedOutput);
|
||||
codedOutput.flush();
|
||||
}
|
||||
|
||||
/** Get the number of bytes required to encode this set. */
|
||||
public int getSerializedSize() {
|
||||
int result = 0;
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
result += entry.getValue().getSerializedSize(entry.getKey());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the set and writes it to {@code output} using
|
||||
* {@code MessageSet} wire format.
|
||||
*/
|
||||
public void writeAsMessageSetTo(final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
entry.getValue().writeAsMessageSetExtensionTo(
|
||||
entry.getKey(), output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this set using
|
||||
* {@code MessageSet} wire format.
|
||||
*/
|
||||
public int getSerializedSizeAsMessageSet() {
|
||||
int result = 0;
|
||||
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
|
||||
result += entry.getValue().getSerializedSizeAsMessageSetExtension(
|
||||
entry.getKey());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
// UnknownFieldSets do not have required fields, so they are always
|
||||
// initialized.
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Parse an {@code UnknownFieldSet} from the given input stream. */
|
||||
public static UnknownFieldSet parseFrom(final CodedInputStream input)
|
||||
throws IOException {
|
||||
return newBuilder().mergeFrom(input).build();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).build();
|
||||
}
|
||||
|
||||
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).build();
|
||||
}
|
||||
|
||||
/** Parse an {@code UnknownFieldSet} from {@code input} and return it. */
|
||||
public static UnknownFieldSet parseFrom(final InputStream input)
|
||||
throws IOException {
|
||||
return newBuilder().mergeFrom(input).build();
|
||||
}
|
||||
|
||||
public Builder newBuilderForType() {
|
||||
return newBuilder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return newBuilder().mergeFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link UnknownFieldSet}s.
|
||||
*
|
||||
* <p>Note that this class maintains {@link Field.Builder}s for all fields
|
||||
* in the set. Thus, adding one element to an existing {@link Field} does not
|
||||
* require making a copy. This is important for efficient parsing of
|
||||
* unknown repeated fields. However, it implies that {@link Field}s cannot
|
||||
* be constructed independently, nor can two {@link UnknownFieldSet}s share
|
||||
* the same {@code Field} object.
|
||||
*
|
||||
* <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
|
||||
*/
|
||||
public static final class Builder implements MessageLite.Builder {
|
||||
// This constructor should never be called directly (except from 'create').
|
||||
private Builder() {}
|
||||
|
||||
private Map<Integer, Field> fields;
|
||||
|
||||
// Optimization: We keep around a builder for the last field that was
|
||||
// modified so that we can efficiently add to it multiple times in a
|
||||
// row (important when parsing an unknown repeated field).
|
||||
private int lastFieldNumber;
|
||||
private Field.Builder lastField;
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.reinitialize();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field builder for the given field number which includes any
|
||||
* values that already exist.
|
||||
*/
|
||||
private Field.Builder getFieldBuilder(final int number) {
|
||||
if (lastField != null) {
|
||||
if (number == lastFieldNumber) {
|
||||
return lastField;
|
||||
}
|
||||
// Note: addField() will reset lastField and lastFieldNumber.
|
||||
addField(lastFieldNumber, lastField.build());
|
||||
}
|
||||
if (number == 0) {
|
||||
return null;
|
||||
} else {
|
||||
final Field existing = fields.get(number);
|
||||
lastFieldNumber = number;
|
||||
lastField = Field.newBuilder();
|
||||
if (existing != null) {
|
||||
lastField.mergeFrom(existing);
|
||||
}
|
||||
return lastField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link UnknownFieldSet} and return it.
|
||||
*
|
||||
* <p>Once {@code build()} has been called, the {@code Builder} will no
|
||||
* longer be usable. Calling any method after {@code build()} will result
|
||||
* in undefined behavior and can cause a {@code NullPointerException} to be
|
||||
* thrown.
|
||||
*/
|
||||
public UnknownFieldSet build() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
final UnknownFieldSet result;
|
||||
if (fields.isEmpty()) {
|
||||
result = getDefaultInstance();
|
||||
} else {
|
||||
result = new UnknownFieldSet(Collections.unmodifiableMap(fields));
|
||||
}
|
||||
fields = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public UnknownFieldSet buildPartial() {
|
||||
// No required fields, so this is the same as build().
|
||||
return build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder clone() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
return UnknownFieldSet.newBuilder().mergeFrom(
|
||||
new UnknownFieldSet(fields));
|
||||
}
|
||||
|
||||
public UnknownFieldSet getDefaultInstanceForType() {
|
||||
return UnknownFieldSet.getDefaultInstance();
|
||||
}
|
||||
|
||||
private void reinitialize() {
|
||||
fields = Collections.emptyMap();
|
||||
lastFieldNumber = 0;
|
||||
lastField = null;
|
||||
}
|
||||
|
||||
/** Reset the builder to an empty set. */
|
||||
public Builder clear() {
|
||||
reinitialize();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the fields from {@code other} into this set. If a field number
|
||||
* exists in both sets, {@code other}'s values for that field will be
|
||||
* appended to the values in this set.
|
||||
*/
|
||||
public Builder mergeFrom(final UnknownFieldSet other) {
|
||||
if (other != getDefaultInstance()) {
|
||||
for (final Map.Entry<Integer, Field> entry : other.fields.entrySet()) {
|
||||
mergeField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to the {@code UnknownFieldSet}. If a field with the same
|
||||
* number already exists, the two are merged.
|
||||
*/
|
||||
public Builder mergeField(final int number, final Field field) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
if (hasField(number)) {
|
||||
getFieldBuilder(number).mergeFrom(field);
|
||||
} else {
|
||||
// Optimization: We could call getFieldBuilder(number).mergeFrom(field)
|
||||
// in this case, but that would create a copy of the Field object.
|
||||
// We'd rather reuse the one passed to us, so call addField() instead.
|
||||
addField(number, field);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for merging a new field containing a single varint
|
||||
* value. This is used in particular when an unknown enum value is
|
||||
* encountered.
|
||||
*/
|
||||
public Builder mergeVarintField(final int number, final int value) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
getFieldBuilder(number).addVarint(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Check if the given field number is present in the set. */
|
||||
public boolean hasField(final int number) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
return number == lastFieldNumber || fields.containsKey(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to the {@code UnknownFieldSet}. If a field with the same
|
||||
* number already exists, it is removed.
|
||||
*/
|
||||
public Builder addField(final int number, final Field field) {
|
||||
if (number == 0) {
|
||||
throw new IllegalArgumentException("Zero is not a valid field number.");
|
||||
}
|
||||
if (lastField != null && lastFieldNumber == number) {
|
||||
// Discard this.
|
||||
lastField = null;
|
||||
lastFieldNumber = 0;
|
||||
}
|
||||
if (fields.isEmpty()) {
|
||||
fields = new TreeMap<Integer,Field>();
|
||||
}
|
||||
fields.put(number, field);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all present {@code Field}s as an immutable {@code Map}. If more
|
||||
* fields are added, the changes may or may not be reflected in this map.
|
||||
*/
|
||||
public Map<Integer, Field> asMap() {
|
||||
getFieldBuilder(0); // Force lastField to be built.
|
||||
return Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an entire message from {@code input} and merge its fields into
|
||||
* this set.
|
||||
*/
|
||||
public Builder mergeFrom(final CodedInputStream input) throws IOException {
|
||||
while (true) {
|
||||
final int tag = input.readTag();
|
||||
if (tag == 0 || !mergeFieldFrom(tag, input)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single field from {@code input} and merge it into this set.
|
||||
* @param tag The field's tag number, which was already parsed.
|
||||
* @return {@code false} if the tag is an end group tag.
|
||||
*/
|
||||
public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
|
||||
throws IOException {
|
||||
final int number = WireFormat.getTagFieldNumber(tag);
|
||||
switch (WireFormat.getTagWireType(tag)) {
|
||||
case WireFormat.WIRETYPE_VARINT:
|
||||
getFieldBuilder(number).addVarint(input.readInt64());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_FIXED64:
|
||||
getFieldBuilder(number).addFixed64(input.readFixed64());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
|
||||
getFieldBuilder(number).addLengthDelimited(input.readBytes());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_START_GROUP:
|
||||
final Builder subBuilder = newBuilder();
|
||||
input.readGroup(number, subBuilder,
|
||||
ExtensionRegistry.getEmptyRegistry());
|
||||
getFieldBuilder(number).addGroup(subBuilder.build());
|
||||
return true;
|
||||
case WireFormat.WIRETYPE_END_GROUP:
|
||||
return false;
|
||||
case WireFormat.WIRETYPE_FIXED32:
|
||||
getFieldBuilder(number).addFixed32(input.readFixed32());
|
||||
return true;
|
||||
default:
|
||||
throw InvalidProtocolBufferException.invalidWireType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = data.newCodedInput();
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (final InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a ByteString threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (final InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an {@code UnknownFieldSet} from {@code input} and merge it with the
|
||||
* set being built. This is just a small wrapper around
|
||||
* {@link #mergeFrom(CodedInputStream)}.
|
||||
*/
|
||||
public Builder mergeFrom(final InputStream input) throws IOException {
|
||||
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
|
||||
mergeFrom(codedInput);
|
||||
codedInput.checkLastTagWas(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(InputStream input)
|
||||
throws IOException {
|
||||
final int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return false;
|
||||
}
|
||||
final int size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
final InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
mergeFrom(limitedInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mergeDelimitedFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeDelimitedFrom(input);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
CodedInputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(input);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
ByteString data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(byte[] data, int off, int len)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
final CodedInputStream input =
|
||||
CodedInputStream.newInstance(data, off, len);
|
||||
mergeFrom(input);
|
||||
input.checkLastTagWas(0);
|
||||
return this;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Reading from a byte array threw an IOException (should " +
|
||||
"never happen).", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
byte[] data,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
byte[] data, int off, int len,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(data, off, len);
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry) throws IOException {
|
||||
// UnknownFieldSet has no extensions.
|
||||
return mergeFrom(input);
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
// UnknownFieldSets do not have required fields, so they are always
|
||||
// initialized.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single field in an {@code UnknownFieldSet}.
|
||||
*
|
||||
* <p>A {@code Field} consists of five lists of values. The lists correspond
|
||||
* to the five "wire types" used in the protocol buffer binary format.
|
||||
* The wire type of each field can be determined from the encoded form alone,
|
||||
* without knowing the field's declared type. So, we are able to parse
|
||||
* unknown values at least this far and separate them. Normally, only one
|
||||
* of the five lists will contain any values, since it is impossible to
|
||||
* define a valid message type that declares two different types for the
|
||||
* same field number. However, the code is designed to allow for the case
|
||||
* where the same unknown field number is encountered using multiple different
|
||||
* wire types.
|
||||
*
|
||||
* <p>{@code Field} is an immutable class. To construct one, you must use a
|
||||
* {@link Builder}.
|
||||
*
|
||||
* @see UnknownFieldSet
|
||||
*/
|
||||
public static final class Field {
|
||||
private Field() {}
|
||||
|
||||
/** Construct a new {@link Builder}. */
|
||||
public static Builder newBuilder() {
|
||||
return Builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link Builder} and initialize it to a copy of
|
||||
* {@code copyFrom}.
|
||||
*/
|
||||
public static Builder newBuilder(final Field copyFrom) {
|
||||
return newBuilder().mergeFrom(copyFrom);
|
||||
}
|
||||
|
||||
/** Get an empty {@code Field}. */
|
||||
public static Field getDefaultInstance() {
|
||||
return fieldDefaultInstance;
|
||||
}
|
||||
private static final Field fieldDefaultInstance = newBuilder().build();
|
||||
|
||||
/** Get the list of varint values for this field. */
|
||||
public List<Long> getVarintList() { return varint; }
|
||||
|
||||
/** Get the list of fixed32 values for this field. */
|
||||
public List<Integer> getFixed32List() { return fixed32; }
|
||||
|
||||
/** Get the list of fixed64 values for this field. */
|
||||
public List<Long> getFixed64List() { return fixed64; }
|
||||
|
||||
/** Get the list of length-delimited values for this field. */
|
||||
public List<ByteString> getLengthDelimitedList() { return lengthDelimited; }
|
||||
|
||||
/**
|
||||
* Get the list of embedded group values for this field. These are
|
||||
* represented using {@link UnknownFieldSet}s rather than {@link Message}s
|
||||
* since the group's type is presumably unknown.
|
||||
*/
|
||||
public List<UnknownFieldSet> getGroupList() { return group; }
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Field)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(getIdentityArray(),
|
||||
((Field) other).getIdentityArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getIdentityArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of objects to be used to uniquely identify this
|
||||
* {@link Field} instance.
|
||||
*/
|
||||
private Object[] getIdentityArray() {
|
||||
return new Object[] {
|
||||
varint,
|
||||
fixed32,
|
||||
fixed64,
|
||||
lengthDelimited,
|
||||
group};
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the field, including field number, and writes it to
|
||||
* {@code output}.
|
||||
*/
|
||||
public void writeTo(final int fieldNumber, final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final long value : varint) {
|
||||
output.writeUInt64(fieldNumber, value);
|
||||
}
|
||||
for (final int value : fixed32) {
|
||||
output.writeFixed32(fieldNumber, value);
|
||||
}
|
||||
for (final long value : fixed64) {
|
||||
output.writeFixed64(fieldNumber, value);
|
||||
}
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
output.writeBytes(fieldNumber, value);
|
||||
}
|
||||
for (final UnknownFieldSet value : group) {
|
||||
output.writeGroup(fieldNumber, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this field, including field
|
||||
* number.
|
||||
*/
|
||||
public int getSerializedSize(final int fieldNumber) {
|
||||
int result = 0;
|
||||
for (final long value : varint) {
|
||||
result += CodedOutputStream.computeUInt64Size(fieldNumber, value);
|
||||
}
|
||||
for (final int value : fixed32) {
|
||||
result += CodedOutputStream.computeFixed32Size(fieldNumber, value);
|
||||
}
|
||||
for (final long value : fixed64) {
|
||||
result += CodedOutputStream.computeFixed64Size(fieldNumber, value);
|
||||
}
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
result += CodedOutputStream.computeBytesSize(fieldNumber, value);
|
||||
}
|
||||
for (final UnknownFieldSet value : group) {
|
||||
result += CodedOutputStream.computeGroupSize(fieldNumber, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the field, including field number, and writes it to
|
||||
* {@code output}, using {@code MessageSet} wire format.
|
||||
*/
|
||||
public void writeAsMessageSetExtensionTo(
|
||||
final int fieldNumber,
|
||||
final CodedOutputStream output)
|
||||
throws IOException {
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
output.writeRawMessageSetExtension(fieldNumber, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes required to encode this field, including field
|
||||
* number, using {@code MessageSet} wire format.
|
||||
*/
|
||||
public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) {
|
||||
int result = 0;
|
||||
for (final ByteString value : lengthDelimited) {
|
||||
result += CodedOutputStream.computeRawMessageSetExtensionSize(
|
||||
fieldNumber, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Long> varint;
|
||||
private List<Integer> fixed32;
|
||||
private List<Long> fixed64;
|
||||
private List<ByteString> lengthDelimited;
|
||||
private List<UnknownFieldSet> group;
|
||||
|
||||
/**
|
||||
* Used to build a {@link Field} within an {@link UnknownFieldSet}.
|
||||
*
|
||||
* <p>Use {@link Field#newBuilder()} to construct a {@code Builder}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
// This constructor should never be called directly (except from 'create').
|
||||
private Builder() {}
|
||||
|
||||
private static Builder create() {
|
||||
Builder builder = new Builder();
|
||||
builder.result = new Field();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private Field result;
|
||||
|
||||
/**
|
||||
* Build the field. After {@code build()} has been called, the
|
||||
* {@code Builder} is no longer usable. Calling any other method will
|
||||
* result in undefined behavior and can cause a
|
||||
* {@code NullPointerException} to be thrown.
|
||||
*/
|
||||
public Field build() {
|
||||
if (result.varint == null) {
|
||||
result.varint = Collections.emptyList();
|
||||
} else {
|
||||
result.varint = Collections.unmodifiableList(result.varint);
|
||||
}
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = Collections.emptyList();
|
||||
} else {
|
||||
result.fixed32 = Collections.unmodifiableList(result.fixed32);
|
||||
}
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = Collections.emptyList();
|
||||
} else {
|
||||
result.fixed64 = Collections.unmodifiableList(result.fixed64);
|
||||
}
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = Collections.emptyList();
|
||||
} else {
|
||||
result.lengthDelimited =
|
||||
Collections.unmodifiableList(result.lengthDelimited);
|
||||
}
|
||||
if (result.group == null) {
|
||||
result.group = Collections.emptyList();
|
||||
} else {
|
||||
result.group = Collections.unmodifiableList(result.group);
|
||||
}
|
||||
|
||||
final Field returnMe = result;
|
||||
result = null;
|
||||
return returnMe;
|
||||
}
|
||||
|
||||
/** Discard the field's contents. */
|
||||
public Builder clear() {
|
||||
result = new Field();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the values in {@code other} into this field. For each list
|
||||
* of values, {@code other}'s values are append to the ones in this
|
||||
* field.
|
||||
*/
|
||||
public Builder mergeFrom(final Field other) {
|
||||
if (!other.varint.isEmpty()) {
|
||||
if (result.varint == null) {
|
||||
result.varint = new ArrayList<Long>();
|
||||
}
|
||||
result.varint.addAll(other.varint);
|
||||
}
|
||||
if (!other.fixed32.isEmpty()) {
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = new ArrayList<Integer>();
|
||||
}
|
||||
result.fixed32.addAll(other.fixed32);
|
||||
}
|
||||
if (!other.fixed64.isEmpty()) {
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = new ArrayList<Long>();
|
||||
}
|
||||
result.fixed64.addAll(other.fixed64);
|
||||
}
|
||||
if (!other.lengthDelimited.isEmpty()) {
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = new ArrayList<ByteString>();
|
||||
}
|
||||
result.lengthDelimited.addAll(other.lengthDelimited);
|
||||
}
|
||||
if (!other.group.isEmpty()) {
|
||||
if (result.group == null) {
|
||||
result.group = new ArrayList<UnknownFieldSet>();
|
||||
}
|
||||
result.group.addAll(other.group);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a varint value. */
|
||||
public Builder addVarint(final long value) {
|
||||
if (result.varint == null) {
|
||||
result.varint = new ArrayList<Long>();
|
||||
}
|
||||
result.varint.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a fixed32 value. */
|
||||
public Builder addFixed32(final int value) {
|
||||
if (result.fixed32 == null) {
|
||||
result.fixed32 = new ArrayList<Integer>();
|
||||
}
|
||||
result.fixed32.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a fixed64 value. */
|
||||
public Builder addFixed64(final long value) {
|
||||
if (result.fixed64 == null) {
|
||||
result.fixed64 = new ArrayList<Long>();
|
||||
}
|
||||
result.fixed64.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add a length-delimited value. */
|
||||
public Builder addLengthDelimited(final ByteString value) {
|
||||
if (result.lengthDelimited == null) {
|
||||
result.lengthDelimited = new ArrayList<ByteString>();
|
||||
}
|
||||
result.lengthDelimited.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add an embedded group. */
|
||||
public Builder addGroup(final UnknownFieldSet value) {
|
||||
if (result.group == null) {
|
||||
result.group = new ArrayList<UnknownFieldSet>();
|
||||
}
|
||||
result.group.add(value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser to implement MessageLite interface.
|
||||
*/
|
||||
public static final class Parser extends AbstractParser<UnknownFieldSet> {
|
||||
public UnknownFieldSet parsePartialFrom(
|
||||
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
Builder builder = newBuilder();
|
||||
try {
|
||||
builder.mergeFrom(input);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(builder.buildPartial());
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage())
|
||||
.setUnfinishedMessage(builder.buildPartial());
|
||||
}
|
||||
return builder.buildPartial();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Parser PARSER = new Parser();
|
||||
public final Parser getParserForType() {
|
||||
return PARSER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LazyStringList} that wraps another
|
||||
* {@link LazyStringList} such that it cannot be modified via the wrapper.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class UnmodifiableLazyStringList extends AbstractList<String>
|
||||
implements LazyStringList, RandomAccess {
|
||||
|
||||
private final LazyStringList list;
|
||||
|
||||
public UnmodifiableLazyStringList(LazyStringList list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public ByteString getByteString(int index) {
|
||||
return list.getByteString(index);
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void add(ByteString element) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public ListIterator<String> listIterator(final int index) {
|
||||
return new ListIterator<String>() {
|
||||
ListIterator<String> iter = list.listIterator(index);
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasPrevious() {
|
||||
return iter.hasPrevious();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String previous() {
|
||||
return iter.previous();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int nextIndex() {
|
||||
return iter.nextIndex();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public int previousIndex() {
|
||||
return iter.previousIndex();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void set(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void add(String o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return new Iterator<String>() {
|
||||
Iterator<String> iter = list.iterator();
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public String next() {
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public List<?> getUnderlyingElements() {
|
||||
// The returned value is already unmodifiable.
|
||||
return list.getUnderlyingElements();
|
||||
}
|
||||
}
|
||||
349
java/src/main/java/com/google/protobuf/Utf8.java
Normal file
349
java/src/main/java/com/google/protobuf/Utf8.java
Normal file
@@ -0,0 +1,349 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* A set of low-level, high-performance static utility methods related
|
||||
* to the UTF-8 character encoding. This class has no dependencies
|
||||
* outside of the core JDK libraries.
|
||||
*
|
||||
* <p>There are several variants of UTF-8. The one implemented by
|
||||
* this class is the restricted definition of UTF-8 introduced in
|
||||
* Unicode 3.1, which mandates the rejection of "overlong" byte
|
||||
* sequences as well as rejection of 3-byte surrogate codepoint byte
|
||||
* sequences. Note that the UTF-8 decoder included in Oracle's JDK
|
||||
* has been modified to also reject "overlong" byte sequences, but (as
|
||||
* of 2011) still accepts 3-byte surrogate codepoint byte sequences.
|
||||
*
|
||||
* <p>The byte sequences considered valid by this class are exactly
|
||||
* those that can be roundtrip converted to Strings and back to bytes
|
||||
* using the UTF-8 charset, without loss: <pre> {@code
|
||||
* Arrays.equals(bytes, new String(bytes, "UTF-8").getBytes("UTF-8"))
|
||||
* }</pre>
|
||||
*
|
||||
* <p>See the Unicode Standard,</br>
|
||||
* Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
|
||||
* Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
|
||||
*
|
||||
* <p>This class supports decoding of partial byte sequences, so that the
|
||||
* bytes in a complete UTF-8 byte sequences can be stored in multiple
|
||||
* segments. Methods typically return {@link #MALFORMED} if the partial
|
||||
* byte sequence is definitely not well-formed, {@link #COMPLETE} if it is
|
||||
* well-formed in the absence of additional input, or if the byte sequence
|
||||
* apparently terminated in the middle of a character, an opaque integer
|
||||
* "state" value containing enough information to decode the character when
|
||||
* passed to a subsequent invocation of a partial decoding method.
|
||||
*
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
final class Utf8 {
|
||||
private Utf8() {}
|
||||
|
||||
/**
|
||||
* State value indicating that the byte sequence is well-formed and
|
||||
* complete (no further bytes are needed to complete a character).
|
||||
*/
|
||||
public static final int COMPLETE = 0;
|
||||
|
||||
/**
|
||||
* State value indicating that the byte sequence is definitely not
|
||||
* well-formed.
|
||||
*/
|
||||
public static final int MALFORMED = -1;
|
||||
|
||||
// Other state values include the partial bytes of the incomplete
|
||||
// character to be decoded in the simplest way: we pack the bytes
|
||||
// into the state int in little-endian order. For example:
|
||||
//
|
||||
// int state = byte1 ^ (byte2 << 8) ^ (byte3 << 16);
|
||||
//
|
||||
// Such a state is unpacked thus (note the ~ operation for byte2 to
|
||||
// undo byte1's sign-extension bits):
|
||||
//
|
||||
// int byte1 = (byte) state;
|
||||
// int byte2 = (byte) ~(state >> 8);
|
||||
// int byte3 = (byte) (state >> 16);
|
||||
//
|
||||
// We cannot store a zero byte in the state because it would be
|
||||
// indistinguishable from the absence of a byte. But we don't need
|
||||
// to, because partial bytes must always be negative. When building
|
||||
// a state, we ensure that byte1 is negative and subsequent bytes
|
||||
// are valid trailing bytes.
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given byte array is a well-formed
|
||||
* UTF-8 byte sequence.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to a call to {@code
|
||||
* isValidUtf8(bytes, 0, bytes.length)}.
|
||||
*/
|
||||
public static boolean isValidUtf8(byte[] bytes) {
|
||||
return isValidUtf8(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given byte array slice is a
|
||||
* well-formed UTF-8 byte sequence. The range of bytes to be
|
||||
* checked extends from index {@code index}, inclusive, to {@code
|
||||
* limit}, exclusive.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to {@code
|
||||
* partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}.
|
||||
*/
|
||||
public static boolean isValidUtf8(byte[] bytes, int index, int limit) {
|
||||
return partialIsValidUtf8(bytes, index, limit) == COMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given byte array slice is a well-formed,
|
||||
* malformed, or incomplete UTF-8 byte sequence. The range of bytes
|
||||
* to be checked extends from index {@code index}, inclusive, to
|
||||
* {@code limit}, exclusive.
|
||||
*
|
||||
* @param state either {@link Utf8#COMPLETE} (if this is the initial decoding
|
||||
* operation) or the value returned from a call to a partial decoding method
|
||||
* for the previous bytes
|
||||
*
|
||||
* @return {@link #MALFORMED} if the partial byte sequence is
|
||||
* definitely not well-formed, {@link #COMPLETE} if it is well-formed
|
||||
* (no additional input needed), or if the byte sequence is
|
||||
* "incomplete", i.e. apparently terminated in the middle of a character,
|
||||
* an opaque integer "state" value containing enough information to
|
||||
* decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
public static int partialIsValidUtf8(
|
||||
int state, byte[] bytes, int index, int limit) {
|
||||
if (state != COMPLETE) {
|
||||
// The previous decoding operation was incomplete (or malformed).
|
||||
// We look for a well-formed sequence consisting of bytes from
|
||||
// the previous decoding operation (stored in state) together
|
||||
// with bytes from the array slice.
|
||||
//
|
||||
// We expect such "straddler characters" to be rare.
|
||||
|
||||
if (index >= limit) { // No bytes? No progress.
|
||||
return state;
|
||||
}
|
||||
int byte1 = (byte) state;
|
||||
// byte1 is never ASCII.
|
||||
if (byte1 < (byte) 0xE0) {
|
||||
// two-byte form
|
||||
|
||||
// Simultaneously checks for illegal trailing-byte in
|
||||
// leading position and overlong 2-byte form.
|
||||
if (byte1 < (byte) 0xC2 ||
|
||||
// byte2 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else if (byte1 < (byte) 0xF0) {
|
||||
// three-byte form
|
||||
|
||||
// Get byte2 from saved state or array
|
||||
int byte2 = (byte) ~(state >> 8);
|
||||
if (byte2 == 0) {
|
||||
byte2 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2);
|
||||
}
|
||||
}
|
||||
if (byte2 > (byte) 0xBF ||
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
(byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
|
||||
// illegal surrogate codepoint?
|
||||
(byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
// Get byte2 and byte3 from saved state or array
|
||||
int byte2 = (byte) ~(state >> 8);
|
||||
int byte3 = 0;
|
||||
if (byte2 == 0) {
|
||||
byte2 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2);
|
||||
}
|
||||
} else {
|
||||
byte3 = (byte) (state >> 16);
|
||||
}
|
||||
if (byte3 == 0) {
|
||||
byte3 = bytes[index++];
|
||||
if (index >= limit) {
|
||||
return incompleteStateFor(byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
|
||||
// If we were called with state == MALFORMED, then byte1 is 0xFF,
|
||||
// which never occurs in well-formed UTF-8, and so we will return
|
||||
// MALFORMED again below.
|
||||
|
||||
if (byte2 > (byte) 0xBF ||
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
|
||||
// byte3 trailing-byte test
|
||||
byte3 > (byte) 0xBF ||
|
||||
// byte4 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return partialIsValidUtf8(bytes, index, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the given byte array slice is a well-formed,
|
||||
* malformed, or incomplete UTF-8 byte sequence. The range of bytes
|
||||
* to be checked extends from index {@code index}, inclusive, to
|
||||
* {@code limit}, exclusive.
|
||||
*
|
||||
* <p>This is a convenience method, equivalent to a call to {@code
|
||||
* partialIsValidUtf8(Utf8.COMPLETE, bytes, index, limit)}.
|
||||
*
|
||||
* @return {@link #MALFORMED} if the partial byte sequence is
|
||||
* definitely not well-formed, {@link #COMPLETE} if it is well-formed
|
||||
* (no additional input needed), or if the byte sequence is
|
||||
* "incomplete", i.e. apparently terminated in the middle of a character,
|
||||
* an opaque integer "state" value containing enough information to
|
||||
* decode the character when passed to a subsequent invocation of a
|
||||
* partial decoding method.
|
||||
*/
|
||||
public static int partialIsValidUtf8(
|
||||
byte[] bytes, int index, int limit) {
|
||||
// Optimize for 100% ASCII.
|
||||
// Hotspot loves small simple top-level loops like this.
|
||||
while (index < limit && bytes[index] >= 0) {
|
||||
index++;
|
||||
}
|
||||
|
||||
return (index >= limit) ? COMPLETE :
|
||||
partialIsValidUtf8NonAscii(bytes, index, limit);
|
||||
}
|
||||
|
||||
private static int partialIsValidUtf8NonAscii(
|
||||
byte[] bytes, int index, int limit) {
|
||||
for (;;) {
|
||||
int byte1, byte2;
|
||||
|
||||
// Optimize for interior runs of ASCII bytes.
|
||||
do {
|
||||
if (index >= limit) {
|
||||
return COMPLETE;
|
||||
}
|
||||
} while ((byte1 = bytes[index++]) >= 0);
|
||||
|
||||
if (byte1 < (byte) 0xE0) {
|
||||
// two-byte form
|
||||
|
||||
if (index >= limit) {
|
||||
return byte1;
|
||||
}
|
||||
|
||||
// Simultaneously checks for illegal trailing-byte in
|
||||
// leading position and overlong 2-byte form.
|
||||
if (byte1 < (byte) 0xC2 ||
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else if (byte1 < (byte) 0xF0) {
|
||||
// three-byte form
|
||||
|
||||
if (index >= limit - 1) { // incomplete sequence
|
||||
return incompleteStateFor(bytes, index, limit);
|
||||
}
|
||||
if ((byte2 = bytes[index++]) > (byte) 0xBF ||
|
||||
// overlong? 5 most significant bits must not all be zero
|
||||
(byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
|
||||
// check for illegal surrogate codepoints
|
||||
(byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
} else {
|
||||
// four-byte form
|
||||
|
||||
if (index >= limit - 2) { // incomplete sequence
|
||||
return incompleteStateFor(bytes, index, limit);
|
||||
}
|
||||
if ((byte2 = bytes[index++]) > (byte) 0xBF ||
|
||||
// Check that 1 <= plane <= 16. Tricky optimized form of:
|
||||
// if (byte1 > (byte) 0xF4 ||
|
||||
// byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
|
||||
// byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
|
||||
(((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
|
||||
// byte3 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF ||
|
||||
// byte4 trailing-byte test
|
||||
bytes[index++] > (byte) 0xBF) {
|
||||
return MALFORMED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1) {
|
||||
return (byte1 > (byte) 0xF4) ?
|
||||
MALFORMED : byte1;
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1, int byte2) {
|
||||
return (byte1 > (byte) 0xF4 ||
|
||||
byte2 > (byte) 0xBF) ?
|
||||
MALFORMED : byte1 ^ (byte2 << 8);
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(int byte1, int byte2, int byte3) {
|
||||
return (byte1 > (byte) 0xF4 ||
|
||||
byte2 > (byte) 0xBF ||
|
||||
byte3 > (byte) 0xBF) ?
|
||||
MALFORMED : byte1 ^ (byte2 << 8) ^ (byte3 << 16);
|
||||
}
|
||||
|
||||
private static int incompleteStateFor(byte[] bytes, int index, int limit) {
|
||||
int byte1 = bytes[index - 1];
|
||||
switch (limit - index) {
|
||||
case 0: return incompleteStateFor(byte1);
|
||||
case 1: return incompleteStateFor(byte1, bytes[index]);
|
||||
case 2: return incompleteStateFor(byte1, bytes[index], bytes[index + 1]);
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
163
java/src/main/java/com/google/protobuf/WireFormat.java
Normal file
163
java/src/main/java/com/google/protobuf/WireFormat.java
Normal file
@@ -0,0 +1,163 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* This class is used internally by the Protocol Buffer library and generated
|
||||
* message implementations. It is public only because those generated messages
|
||||
* do not reside in the {@code protobuf} package. Others should not use this
|
||||
* class directly.
|
||||
*
|
||||
* This class contains constants and helper functions useful for dealing with
|
||||
* the Protocol Buffer wire format.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public final class WireFormat {
|
||||
// Do not allow instantiation.
|
||||
private WireFormat() {}
|
||||
|
||||
public static final int WIRETYPE_VARINT = 0;
|
||||
public static final int WIRETYPE_FIXED64 = 1;
|
||||
public static final int WIRETYPE_LENGTH_DELIMITED = 2;
|
||||
public static final int WIRETYPE_START_GROUP = 3;
|
||||
public static final int WIRETYPE_END_GROUP = 4;
|
||||
public static final int WIRETYPE_FIXED32 = 5;
|
||||
|
||||
static final int TAG_TYPE_BITS = 3;
|
||||
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
|
||||
|
||||
/** Given a tag value, determines the wire type (the lower 3 bits). */
|
||||
static int getTagWireType(final int tag) {
|
||||
return tag & TAG_TYPE_MASK;
|
||||
}
|
||||
|
||||
/** Given a tag value, determines the field number (the upper 29 bits). */
|
||||
public static int getTagFieldNumber(final int tag) {
|
||||
return tag >>> TAG_TYPE_BITS;
|
||||
}
|
||||
|
||||
/** Makes a tag value given a field number and wire type. */
|
||||
static int makeTag(final int fieldNumber, final int wireType) {
|
||||
return (fieldNumber << TAG_TYPE_BITS) | wireType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link Descriptors.FieldDescriptor.JavaType}. This is
|
||||
* only here to support the lite runtime and should not be used by users.
|
||||
*/
|
||||
public enum JavaType {
|
||||
INT(0),
|
||||
LONG(0L),
|
||||
FLOAT(0F),
|
||||
DOUBLE(0D),
|
||||
BOOLEAN(false),
|
||||
STRING(""),
|
||||
BYTE_STRING(ByteString.EMPTY),
|
||||
ENUM(null),
|
||||
MESSAGE(null);
|
||||
|
||||
JavaType(final Object defaultDefault) {
|
||||
this.defaultDefault = defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default default value for fields of this type, if it's a primitive
|
||||
* type.
|
||||
*/
|
||||
Object getDefaultDefault() {
|
||||
return defaultDefault;
|
||||
}
|
||||
|
||||
private final Object defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lite equivalent to {@link Descriptors.FieldDescriptor.Type}. This is
|
||||
* only here to support the lite runtime and should not be used by users.
|
||||
*/
|
||||
public enum FieldType {
|
||||
DOUBLE (JavaType.DOUBLE , WIRETYPE_FIXED64 ),
|
||||
FLOAT (JavaType.FLOAT , WIRETYPE_FIXED32 ),
|
||||
INT64 (JavaType.LONG , WIRETYPE_VARINT ),
|
||||
UINT64 (JavaType.LONG , WIRETYPE_VARINT ),
|
||||
INT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
FIXED64 (JavaType.LONG , WIRETYPE_FIXED64 ),
|
||||
FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ),
|
||||
BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ),
|
||||
STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED) {
|
||||
public boolean isPackable() { return false; }
|
||||
},
|
||||
UINT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
ENUM (JavaType.ENUM , WIRETYPE_VARINT ),
|
||||
SFIXED32(JavaType.INT , WIRETYPE_FIXED32 ),
|
||||
SFIXED64(JavaType.LONG , WIRETYPE_FIXED64 ),
|
||||
SINT32 (JavaType.INT , WIRETYPE_VARINT ),
|
||||
SINT64 (JavaType.LONG , WIRETYPE_VARINT );
|
||||
|
||||
FieldType(final JavaType javaType, final int wireType) {
|
||||
this.javaType = javaType;
|
||||
this.wireType = wireType;
|
||||
}
|
||||
|
||||
private final JavaType javaType;
|
||||
private final int wireType;
|
||||
|
||||
public JavaType getJavaType() { return javaType; }
|
||||
public int getWireType() { return wireType; }
|
||||
|
||||
public boolean isPackable() { return true; }
|
||||
}
|
||||
|
||||
// Field numbers for fields in MessageSet wire format.
|
||||
static final int MESSAGE_SET_ITEM = 1;
|
||||
static final int MESSAGE_SET_TYPE_ID = 2;
|
||||
static final int MESSAGE_SET_MESSAGE = 3;
|
||||
|
||||
// Tag numbers.
|
||||
static final int MESSAGE_SET_ITEM_TAG =
|
||||
makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP);
|
||||
static final int MESSAGE_SET_ITEM_END_TAG =
|
||||
makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP);
|
||||
static final int MESSAGE_SET_TYPE_ID_TAG =
|
||||
makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT);
|
||||
static final int MESSAGE_SET_MESSAGE_TAG =
|
||||
makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED);
|
||||
}
|
||||
509
java/src/test/java/com/google/protobuf/AbstractMessageTest.java
Normal file
509
java/src/test/java/com/google/protobuf/AbstractMessageTest.java
Normal file
@@ -0,0 +1,509 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredForeign;
|
||||
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AbstractMessage}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class AbstractMessageTest extends TestCase {
|
||||
/**
|
||||
* Extends AbstractMessage and wraps some other message object. The methods
|
||||
* of the Message interface which aren't explicitly implemented by
|
||||
* AbstractMessage are forwarded to the wrapped object. This allows us to
|
||||
* test that AbstractMessage's implementations work even if the wrapped
|
||||
* object does not use them.
|
||||
*/
|
||||
private static class AbstractMessageWrapper extends AbstractMessage {
|
||||
private final Message wrappedMessage;
|
||||
|
||||
public AbstractMessageWrapper(Message wrappedMessage) {
|
||||
this.wrappedMessage = wrappedMessage;
|
||||
}
|
||||
|
||||
public Descriptors.Descriptor getDescriptorForType() {
|
||||
return wrappedMessage.getDescriptorForType();
|
||||
}
|
||||
public AbstractMessageWrapper getDefaultInstanceForType() {
|
||||
return new AbstractMessageWrapper(
|
||||
wrappedMessage.getDefaultInstanceForType());
|
||||
}
|
||||
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
|
||||
return wrappedMessage.getAllFields();
|
||||
}
|
||||
public boolean hasField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedMessage.hasField(field);
|
||||
}
|
||||
public Object getField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedMessage.getField(field);
|
||||
}
|
||||
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
|
||||
return wrappedMessage.getRepeatedFieldCount(field);
|
||||
}
|
||||
public Object getRepeatedField(
|
||||
Descriptors.FieldDescriptor field, int index) {
|
||||
return wrappedMessage.getRepeatedField(field, index);
|
||||
}
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return wrappedMessage.getUnknownFields();
|
||||
}
|
||||
public Builder newBuilderForType() {
|
||||
return new Builder(wrappedMessage.newBuilderForType());
|
||||
}
|
||||
public Builder toBuilder() {
|
||||
return new Builder(wrappedMessage.toBuilder());
|
||||
}
|
||||
|
||||
static class Builder extends AbstractMessage.Builder<Builder> {
|
||||
private final Message.Builder wrappedBuilder;
|
||||
|
||||
public Builder(Message.Builder wrappedBuilder) {
|
||||
this.wrappedBuilder = wrappedBuilder;
|
||||
}
|
||||
|
||||
public AbstractMessageWrapper build() {
|
||||
return new AbstractMessageWrapper(wrappedBuilder.build());
|
||||
}
|
||||
public AbstractMessageWrapper buildPartial() {
|
||||
return new AbstractMessageWrapper(wrappedBuilder.buildPartial());
|
||||
}
|
||||
public Builder clone() {
|
||||
return new Builder(wrappedBuilder.clone());
|
||||
}
|
||||
public boolean isInitialized() {
|
||||
return clone().buildPartial().isInitialized();
|
||||
}
|
||||
public Descriptors.Descriptor getDescriptorForType() {
|
||||
return wrappedBuilder.getDescriptorForType();
|
||||
}
|
||||
public AbstractMessageWrapper getDefaultInstanceForType() {
|
||||
return new AbstractMessageWrapper(
|
||||
wrappedBuilder.getDefaultInstanceForType());
|
||||
}
|
||||
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
|
||||
return wrappedBuilder.getAllFields();
|
||||
}
|
||||
public Builder newBuilderForField(Descriptors.FieldDescriptor field) {
|
||||
return new Builder(wrappedBuilder.newBuilderForField(field));
|
||||
}
|
||||
public boolean hasField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedBuilder.hasField(field);
|
||||
}
|
||||
public Object getField(Descriptors.FieldDescriptor field) {
|
||||
return wrappedBuilder.getField(field);
|
||||
}
|
||||
public Builder setField(Descriptors.FieldDescriptor field, Object value) {
|
||||
wrappedBuilder.setField(field, value);
|
||||
return this;
|
||||
}
|
||||
public Builder clearField(Descriptors.FieldDescriptor field) {
|
||||
wrappedBuilder.clearField(field);
|
||||
return this;
|
||||
}
|
||||
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
|
||||
return wrappedBuilder.getRepeatedFieldCount(field);
|
||||
}
|
||||
public Object getRepeatedField(
|
||||
Descriptors.FieldDescriptor field, int index) {
|
||||
return wrappedBuilder.getRepeatedField(field, index);
|
||||
}
|
||||
public Builder setRepeatedField(Descriptors.FieldDescriptor field,
|
||||
int index, Object value) {
|
||||
wrappedBuilder.setRepeatedField(field, index, value);
|
||||
return this;
|
||||
}
|
||||
public Builder addRepeatedField(
|
||||
Descriptors.FieldDescriptor field, Object value) {
|
||||
wrappedBuilder.addRepeatedField(field, value);
|
||||
return this;
|
||||
}
|
||||
public UnknownFieldSet getUnknownFields() {
|
||||
return wrappedBuilder.getUnknownFields();
|
||||
}
|
||||
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
|
||||
wrappedBuilder.setUnknownFields(unknownFields);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Message.Builder getFieldBuilder(FieldDescriptor field) {
|
||||
return wrappedBuilder.getFieldBuilder(field);
|
||||
}
|
||||
}
|
||||
public Parser<? extends Message> getParserForType() {
|
||||
return wrappedMessage.getParserForType();
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
TestUtil.ReflectionTester reflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null);
|
||||
|
||||
TestUtil.ReflectionTester extensionsReflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
|
||||
public void testClear() throws Exception {
|
||||
AbstractMessageWrapper message =
|
||||
new AbstractMessageWrapper.Builder(
|
||||
TestAllTypes.newBuilder(TestUtil.getAllSet()))
|
||||
.clear().build();
|
||||
TestUtil.assertClear((TestAllTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testCopy() throws Exception {
|
||||
AbstractMessageWrapper message =
|
||||
new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder())
|
||||
.mergeFrom(TestUtil.getAllSet()).build();
|
||||
TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testSerializedSize() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet());
|
||||
|
||||
assertEquals(message.getSerializedSize(),
|
||||
abstractMessage.getSerializedSize());
|
||||
}
|
||||
|
||||
public void testSerialization() throws Exception {
|
||||
Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet());
|
||||
|
||||
TestUtil.assertAllFieldsSet(
|
||||
TestAllTypes.parseFrom(abstractMessage.toByteString()));
|
||||
|
||||
assertEquals(TestUtil.getAllSet().toByteString(),
|
||||
abstractMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testParsing() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getAllSet().toByteString()).build();
|
||||
TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testParsingUninitialized() throws Exception {
|
||||
TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
|
||||
builder.getOptionalMessageBuilder().setDummy2(10);
|
||||
ByteString bytes = builder.buildPartial().toByteString();
|
||||
Message.Builder abstractMessageBuilder =
|
||||
new AbstractMessageWrapper.Builder(TestRequiredForeign.newBuilder());
|
||||
// mergeFrom() should not throw initialization error.
|
||||
abstractMessageBuilder.mergeFrom(bytes).buildPartial();
|
||||
try {
|
||||
abstractMessageBuilder.mergeFrom(bytes).build();
|
||||
fail();
|
||||
} catch (UninitializedMessageException ex) {
|
||||
// pass
|
||||
}
|
||||
|
||||
// test DynamicMessage directly.
|
||||
Message.Builder dynamicMessageBuilder = DynamicMessage.newBuilder(
|
||||
TestRequiredForeign.getDescriptor());
|
||||
// mergeFrom() should not throw initialization error.
|
||||
dynamicMessageBuilder.mergeFrom(bytes).buildPartial();
|
||||
try {
|
||||
dynamicMessageBuilder.mergeFrom(bytes).build();
|
||||
fail();
|
||||
} catch (UninitializedMessageException ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testPackedSerialization() throws Exception {
|
||||
Message abstractMessage =
|
||||
new AbstractMessageWrapper(TestUtil.getPackedSet());
|
||||
|
||||
TestUtil.assertPackedFieldsSet(
|
||||
TestPackedTypes.parseFrom(abstractMessage.toByteString()));
|
||||
|
||||
assertEquals(TestUtil.getPackedSet().toByteString(),
|
||||
abstractMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testPackedParsing() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build();
|
||||
TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testUnpackedSerialization() throws Exception {
|
||||
Message abstractMessage =
|
||||
new AbstractMessageWrapper(TestUtil.getUnpackedSet());
|
||||
|
||||
TestUtil.assertUnpackedFieldsSet(
|
||||
TestUnpackedTypes.parseFrom(abstractMessage.toByteString()));
|
||||
|
||||
assertEquals(TestUtil.getUnpackedSet().toByteString(),
|
||||
abstractMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testParsePackedToUnpacked() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build();
|
||||
TestUtil.assertUnpackedFieldsSet(
|
||||
(TestUnpackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testParseUnpackedToPacked() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build();
|
||||
TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testUnpackedParsing() throws Exception {
|
||||
AbstractMessageWrapper.Builder builder =
|
||||
new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder());
|
||||
AbstractMessageWrapper message =
|
||||
builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build();
|
||||
TestUtil.assertUnpackedFieldsSet(
|
||||
(TestUnpackedTypes) message.wrappedMessage);
|
||||
}
|
||||
|
||||
public void testOptimizedForSize() throws Exception {
|
||||
// We're mostly only checking that this class was compiled successfully.
|
||||
TestOptimizedForSize message =
|
||||
TestOptimizedForSize.newBuilder().setI(1).build();
|
||||
message = TestOptimizedForSize.parseFrom(message.toByteString());
|
||||
assertEquals(2, message.getSerializedSize());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for isInitialized().
|
||||
|
||||
private static final TestRequired TEST_REQUIRED_UNINITIALIZED =
|
||||
TestRequired.getDefaultInstance();
|
||||
private static final TestRequired TEST_REQUIRED_INITIALIZED =
|
||||
TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
|
||||
|
||||
public void testIsInitialized() throws Exception {
|
||||
TestRequired.Builder builder = TestRequired.newBuilder();
|
||||
AbstractMessageWrapper.Builder abstractBuilder =
|
||||
new AbstractMessageWrapper.Builder(builder);
|
||||
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals("a, b, c", abstractBuilder.getInitializationErrorString());
|
||||
builder.setA(1);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals("b, c", abstractBuilder.getInitializationErrorString());
|
||||
builder.setB(1);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals("c", abstractBuilder.getInitializationErrorString());
|
||||
builder.setC(1);
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
}
|
||||
|
||||
public void testForeignIsInitialized() throws Exception {
|
||||
TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
|
||||
AbstractMessageWrapper.Builder abstractBuilder =
|
||||
new AbstractMessageWrapper.Builder(builder);
|
||||
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals(
|
||||
"optional_message.a, optional_message.b, optional_message.c",
|
||||
abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(abstractBuilder.isInitialized());
|
||||
assertEquals(
|
||||
"repeated_message[0].a, repeated_message[0].b, repeated_message[0].c",
|
||||
abstractBuilder.getInitializationErrorString());
|
||||
|
||||
builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(abstractBuilder.isInitialized());
|
||||
assertEquals("", abstractBuilder.getInitializationErrorString());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for mergeFrom
|
||||
|
||||
static final TestAllTypes MERGE_SOURCE =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalString("foo")
|
||||
.setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedString("bar")
|
||||
.build();
|
||||
|
||||
static final TestAllTypes MERGE_DEST =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt64(2)
|
||||
.setOptionalString("baz")
|
||||
.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build())
|
||||
.addRepeatedString("qux")
|
||||
.build();
|
||||
|
||||
static final String MERGE_RESULT_TEXT =
|
||||
"optional_int32: 1\n" +
|
||||
"optional_int64: 2\n" +
|
||||
"optional_string: \"foo\"\n" +
|
||||
"optional_foreign_message {\n" +
|
||||
" c: 3\n" +
|
||||
"}\n" +
|
||||
"repeated_string: \"qux\"\n" +
|
||||
"repeated_string: \"bar\"\n";
|
||||
|
||||
public void testMergeFrom() throws Exception {
|
||||
AbstractMessageWrapper result =
|
||||
new AbstractMessageWrapper.Builder(
|
||||
TestAllTypes.newBuilder(MERGE_DEST))
|
||||
.mergeFrom(MERGE_SOURCE).build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Tests for equals and hashCode
|
||||
|
||||
public void testEqualsAndHashCode() throws Exception {
|
||||
TestAllTypes a = TestUtil.getAllSet();
|
||||
TestAllTypes b = TestAllTypes.newBuilder().build();
|
||||
TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build();
|
||||
TestAllTypes d = TestAllTypes.newBuilder(c).addRepeatedString("y").build();
|
||||
TestAllExtensions e = TestUtil.getAllExtensionsSet();
|
||||
TestAllExtensions f = TestAllExtensions.newBuilder(e)
|
||||
.addExtension(UnittestProto.repeatedInt32Extension, 999).build();
|
||||
|
||||
checkEqualsIsConsistent(a);
|
||||
checkEqualsIsConsistent(b);
|
||||
checkEqualsIsConsistent(c);
|
||||
checkEqualsIsConsistent(d);
|
||||
checkEqualsIsConsistent(e);
|
||||
checkEqualsIsConsistent(f);
|
||||
|
||||
checkNotEqual(a, b);
|
||||
checkNotEqual(a, c);
|
||||
checkNotEqual(a, d);
|
||||
checkNotEqual(a, e);
|
||||
checkNotEqual(a, f);
|
||||
|
||||
checkNotEqual(b, c);
|
||||
checkNotEqual(b, d);
|
||||
checkNotEqual(b, e);
|
||||
checkNotEqual(b, f);
|
||||
|
||||
checkNotEqual(c, d);
|
||||
checkNotEqual(c, e);
|
||||
checkNotEqual(c, f);
|
||||
|
||||
checkNotEqual(d, e);
|
||||
checkNotEqual(d, f);
|
||||
|
||||
checkNotEqual(e, f);
|
||||
|
||||
// Deserializing into the TestEmptyMessage such that every field
|
||||
// is an {@link UnknownFieldSet.Field}.
|
||||
UnittestProto.TestEmptyMessage eUnknownFields =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
|
||||
UnittestProto.TestEmptyMessage fUnknownFields =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(f.toByteArray());
|
||||
checkNotEqual(eUnknownFields, fUnknownFields);
|
||||
checkEqualsIsConsistent(eUnknownFields);
|
||||
checkEqualsIsConsistent(fUnknownFields);
|
||||
|
||||
// Subsequent reconstitutions should be identical
|
||||
UnittestProto.TestEmptyMessage eUnknownFields2 =
|
||||
UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
|
||||
checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the given proto has symmetric equals and hashCode methods.
|
||||
*/
|
||||
private void checkEqualsIsConsistent(Message message) {
|
||||
// Object should be equal to itself.
|
||||
assertEquals(message, message);
|
||||
|
||||
// Object should be equal to a dynamic copy of itself.
|
||||
DynamicMessage dynamic = DynamicMessage.newBuilder(message).build();
|
||||
checkEqualsIsConsistent(message, dynamic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given protos are equal and have the same hash code.
|
||||
*/
|
||||
private void checkEqualsIsConsistent(Message message1, Message message2) {
|
||||
assertEquals(message1, message2);
|
||||
assertEquals(message2, message1);
|
||||
assertEquals(message2.hashCode(), message1.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given protos are not equal and have different hash codes.
|
||||
*
|
||||
* @warning It's valid for non-equal objects to have the same hash code, so
|
||||
* this test is stricter than it needs to be. However, this should happen
|
||||
* relatively rarely.
|
||||
*/
|
||||
private void checkNotEqual(Message m1, Message m2) {
|
||||
String equalsError = String.format("%s should not be equal to %s", m1, m2);
|
||||
assertFalse(equalsError, m1.equals(m2));
|
||||
assertFalse(equalsError, m2.equals(m1));
|
||||
|
||||
assertFalse(
|
||||
String.format("%s should have a different hash code from %s", m1, m2),
|
||||
m1.hashCode() == m2.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* This class tests {@link BoundedByteString}, which extends {@link LiteralByteString},
|
||||
* by inheriting the tests from {@link LiteralByteStringTest}. The only method which
|
||||
* is strange enough that it needs to be overridden here is {@link #testToString()}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class BoundedByteStringTest extends LiteralByteStringTest {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "BoundedByteString";
|
||||
byte[] sourceBytes = ByteStringTest.getTestBytes(2341, 11337766L);
|
||||
int from = 100;
|
||||
int to = sourceBytes.length - 100;
|
||||
stringUnderTest = ByteString.copyFrom(sourceBytes).substring(from, to);
|
||||
referenceBytes = new byte[to - from];
|
||||
System.arraycopy(sourceBytes, from, referenceBytes, 0, to - from);
|
||||
expectedHashCode = 727575887;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
LiteralByteString unicode = new LiteralByteString(testString.getBytes(UTF_8));
|
||||
ByteString chopped = unicode.substring(2, unicode.size() - 6);
|
||||
assertEquals(classUnderTest + ".substring() must have the expected type",
|
||||
classUnderTest, getActualClassName(chopped));
|
||||
|
||||
String roundTripString = chopped.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode bytes must match",
|
||||
testString.substring(2, testString.length() - 6), roundTripString);
|
||||
}
|
||||
}
|
||||
692
java/src/test/java/com/google/protobuf/ByteStringTest.java
Normal file
692
java/src/test/java/com/google/protobuf/ByteStringTest.java
Normal file
@@ -0,0 +1,692 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.ByteString.Output;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Test methods with implementations in {@link ByteString}, plus do some top-level "integration"
|
||||
* tests.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class ByteStringTest extends TestCase {
|
||||
|
||||
private static final String UTF_16 = "UTF-16";
|
||||
|
||||
static byte[] getTestBytes(int size, long seed) {
|
||||
Random random = new Random(seed);
|
||||
byte[] result = new byte[size];
|
||||
random.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] getTestBytes(int size) {
|
||||
return getTestBytes(size, 445566L);
|
||||
}
|
||||
|
||||
private byte[] getTestBytes() {
|
||||
return getTestBytes(1000);
|
||||
}
|
||||
|
||||
// Compare the entire left array with a subset of the right array.
|
||||
private boolean isArrayRange(byte[] left, byte[] right, int rightOffset, int length) {
|
||||
boolean stillEqual = (left.length == length);
|
||||
for (int i = 0; (stillEqual && i < length); ++i) {
|
||||
stillEqual = (left[i] == right[rightOffset + i]);
|
||||
}
|
||||
return stillEqual;
|
||||
}
|
||||
|
||||
// Returns true only if the given two arrays have identical contents.
|
||||
private boolean isArray(byte[] left, byte[] right) {
|
||||
return left.length == right.length && isArrayRange(left, right, 0, left.length);
|
||||
}
|
||||
|
||||
public void testSubstring_BeginIndex() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString substring = ByteString.copyFrom(bytes).substring(500);
|
||||
assertTrue("substring must contain the tail of the string",
|
||||
isArrayRange(substring.toByteArray(), bytes, 500, bytes.length - 500));
|
||||
}
|
||||
|
||||
public void testCopyFrom_BytesOffsetSize() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString byteString = ByteString.copyFrom(bytes, 500, 200);
|
||||
assertTrue("copyFrom sub-range must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), bytes, 500, 200));
|
||||
}
|
||||
|
||||
public void testCopyFrom_Bytes() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString byteString = ByteString.copyFrom(bytes);
|
||||
assertTrue("copyFrom must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), bytes));
|
||||
}
|
||||
|
||||
public void testCopyFrom_ByteBufferSize() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
|
||||
byteBuffer.put(bytes);
|
||||
byteBuffer.position(500);
|
||||
ByteString byteString = ByteString.copyFrom(byteBuffer, 200);
|
||||
assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), bytes, 500, 200));
|
||||
}
|
||||
|
||||
public void testCopyFrom_ByteBuffer() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
|
||||
byteBuffer.put(bytes);
|
||||
byteBuffer.position(500);
|
||||
ByteString byteString = ByteString.copyFrom(byteBuffer);
|
||||
assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), bytes, 500, bytes.length - 500));
|
||||
}
|
||||
|
||||
public void testCopyFrom_StringEncoding() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString byteString = ByteString.copyFrom(testString, UTF_16);
|
||||
byte[] testBytes = testString.getBytes(UTF_16);
|
||||
assertTrue("copyFrom string must respect the charset",
|
||||
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
|
||||
}
|
||||
|
||||
public void testCopyFrom_Utf8() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString byteString = ByteString.copyFromUtf8(testString);
|
||||
byte[] testBytes = testString.getBytes("UTF-8");
|
||||
assertTrue("copyFromUtf8 string must respect the charset",
|
||||
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
|
||||
}
|
||||
|
||||
public void testCopyFrom_Iterable() {
|
||||
byte[] testBytes = getTestBytes(77777, 113344L);
|
||||
final List<ByteString> pieces = makeConcretePieces(testBytes);
|
||||
// Call copyFrom() on a Collection
|
||||
ByteString byteString = ByteString.copyFrom(pieces);
|
||||
assertTrue("copyFrom a List must contain the expected bytes",
|
||||
isArrayRange(byteString.toByteArray(), testBytes, 0, testBytes.length));
|
||||
// Call copyFrom on an iteration that's not a collection
|
||||
ByteString byteStringAlt = ByteString.copyFrom(new Iterable<ByteString>() {
|
||||
public Iterator<ByteString> iterator() {
|
||||
return pieces.iterator();
|
||||
}
|
||||
});
|
||||
assertEquals("copyFrom from an Iteration must contain the expected bytes",
|
||||
byteString, byteStringAlt);
|
||||
}
|
||||
|
||||
public void testCopyTo_TargetOffset() {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString byteString = ByteString.copyFrom(bytes);
|
||||
byte[] target = new byte[bytes.length + 1000];
|
||||
byteString.copyTo(target, 400);
|
||||
assertTrue("copyFrom byteBuffer sub-range must contain the expected bytes",
|
||||
isArrayRange(bytes, target, 400, bytes.length));
|
||||
}
|
||||
|
||||
public void testReadFrom_emptyStream() throws IOException {
|
||||
ByteString byteString =
|
||||
ByteString.readFrom(new ByteArrayInputStream(new byte[0]));
|
||||
assertSame("reading an empty stream must result in the EMPTY constant "
|
||||
+ "byte string", ByteString.EMPTY, byteString);
|
||||
}
|
||||
|
||||
public void testReadFrom_smallStream() throws IOException {
|
||||
assertReadFrom(getTestBytes(10));
|
||||
}
|
||||
|
||||
public void testReadFrom_mutating() throws IOException {
|
||||
byte[] capturedArray = null;
|
||||
EvilInputStream eis = new EvilInputStream();
|
||||
ByteString byteString = ByteString.readFrom(eis);
|
||||
|
||||
capturedArray = eis.capturedArray;
|
||||
byte[] originalValue = byteString.toByteArray();
|
||||
for (int x = 0; x < capturedArray.length; ++x) {
|
||||
capturedArray[x] = (byte) 0;
|
||||
}
|
||||
|
||||
byte[] newValue = byteString.toByteArray();
|
||||
assertTrue("copyFrom byteBuffer must not grant access to underlying array",
|
||||
Arrays.equals(originalValue, newValue));
|
||||
}
|
||||
|
||||
// Tests sizes that are near the rope copy-out threshold.
|
||||
public void testReadFrom_mediumStream() throws IOException {
|
||||
assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE - 1));
|
||||
assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE));
|
||||
assertReadFrom(getTestBytes(ByteString.CONCATENATE_BY_COPY_SIZE + 1));
|
||||
assertReadFrom(getTestBytes(200));
|
||||
}
|
||||
|
||||
// Tests sizes that are over multi-segment rope threshold.
|
||||
public void testReadFrom_largeStream() throws IOException {
|
||||
assertReadFrom(getTestBytes(0x100));
|
||||
assertReadFrom(getTestBytes(0x101));
|
||||
assertReadFrom(getTestBytes(0x110));
|
||||
assertReadFrom(getTestBytes(0x1000));
|
||||
assertReadFrom(getTestBytes(0x1001));
|
||||
assertReadFrom(getTestBytes(0x1010));
|
||||
assertReadFrom(getTestBytes(0x10000));
|
||||
assertReadFrom(getTestBytes(0x10001));
|
||||
assertReadFrom(getTestBytes(0x10010));
|
||||
}
|
||||
|
||||
// Tests sizes that are near the read buffer size.
|
||||
public void testReadFrom_byteBoundaries() throws IOException {
|
||||
final int min = ByteString.MIN_READ_FROM_CHUNK_SIZE;
|
||||
final int max = ByteString.MAX_READ_FROM_CHUNK_SIZE;
|
||||
|
||||
assertReadFrom(getTestBytes(min - 1));
|
||||
assertReadFrom(getTestBytes(min));
|
||||
assertReadFrom(getTestBytes(min + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(min * 2 - 1));
|
||||
assertReadFrom(getTestBytes(min * 2));
|
||||
assertReadFrom(getTestBytes(min * 2 + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(min * 4 - 1));
|
||||
assertReadFrom(getTestBytes(min * 4));
|
||||
assertReadFrom(getTestBytes(min * 4 + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(min * 8 - 1));
|
||||
assertReadFrom(getTestBytes(min * 8));
|
||||
assertReadFrom(getTestBytes(min * 8 + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(max - 1));
|
||||
assertReadFrom(getTestBytes(max));
|
||||
assertReadFrom(getTestBytes(max + 1));
|
||||
|
||||
assertReadFrom(getTestBytes(max * 2 - 1));
|
||||
assertReadFrom(getTestBytes(max * 2));
|
||||
assertReadFrom(getTestBytes(max * 2 + 1));
|
||||
}
|
||||
|
||||
// Tests that IOExceptions propagate through ByteString.readFrom().
|
||||
public void testReadFrom_IOExceptions() {
|
||||
try {
|
||||
ByteString.readFrom(new FailStream());
|
||||
fail("readFrom must throw the underlying IOException");
|
||||
|
||||
} catch (IOException e) {
|
||||
assertEquals("readFrom must throw the expected exception",
|
||||
"synthetic failure", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that ByteString.readFrom works with streams that don't
|
||||
// always fill their buffers.
|
||||
public void testReadFrom_reluctantStream() throws IOException {
|
||||
final byte[] data = getTestBytes(0x1000);
|
||||
|
||||
ByteString byteString = ByteString.readFrom(new ReluctantStream(data));
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), data));
|
||||
|
||||
// Same test as above, but with some specific chunk sizes.
|
||||
assertReadFromReluctantStream(data, 100);
|
||||
assertReadFromReluctantStream(data, 248);
|
||||
assertReadFromReluctantStream(data, 249);
|
||||
assertReadFromReluctantStream(data, 250);
|
||||
assertReadFromReluctantStream(data, 251);
|
||||
assertReadFromReluctantStream(data, 0x1000);
|
||||
assertReadFromReluctantStream(data, 0x1001);
|
||||
}
|
||||
|
||||
// Fails unless ByteString.readFrom reads the bytes correctly from a
|
||||
// reluctant stream with the given chunkSize parameter.
|
||||
private void assertReadFromReluctantStream(byte[] bytes, int chunkSize)
|
||||
throws IOException {
|
||||
ByteString b = ByteString.readFrom(new ReluctantStream(bytes), chunkSize);
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(b.toByteArray(), bytes));
|
||||
}
|
||||
|
||||
// Tests that ByteString.readFrom works with streams that implement
|
||||
// available().
|
||||
public void testReadFrom_available() throws IOException {
|
||||
final byte[] data = getTestBytes(0x1001);
|
||||
|
||||
ByteString byteString = ByteString.readFrom(new AvailableStream(data));
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), data));
|
||||
}
|
||||
|
||||
// Fails unless ByteString.readFrom reads the bytes correctly.
|
||||
private void assertReadFrom(byte[] bytes) throws IOException {
|
||||
ByteString byteString =
|
||||
ByteString.readFrom(new ByteArrayInputStream(bytes));
|
||||
assertTrue("readFrom byte stream must contain the expected bytes",
|
||||
isArray(byteString.toByteArray(), bytes));
|
||||
}
|
||||
|
||||
// A stream that fails when read.
|
||||
private static final class FailStream extends InputStream {
|
||||
@Override public int read() throws IOException {
|
||||
throw new IOException("synthetic failure");
|
||||
}
|
||||
}
|
||||
|
||||
// A stream that simulates blocking by only producing 250 characters
|
||||
// per call to read(byte[]).
|
||||
private static class ReluctantStream extends InputStream {
|
||||
protected final byte[] data;
|
||||
protected int pos = 0;
|
||||
|
||||
public ReluctantStream(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override public int read() {
|
||||
if (pos == data.length) {
|
||||
return -1;
|
||||
} else {
|
||||
return data[pos++];
|
||||
}
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buf) {
|
||||
return read(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buf, int offset, int size) {
|
||||
if (pos == data.length) {
|
||||
return -1;
|
||||
}
|
||||
int count = Math.min(Math.min(size, data.length - pos), 250);
|
||||
System.arraycopy(data, pos, buf, offset, count);
|
||||
pos += count;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but also implements available().
|
||||
private static final class AvailableStream extends ReluctantStream {
|
||||
public AvailableStream(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override public int available() {
|
||||
return Math.min(250, data.length - pos);
|
||||
}
|
||||
}
|
||||
|
||||
// A stream which exposes the byte array passed into read(byte[], int, int).
|
||||
private static class EvilInputStream extends InputStream {
|
||||
public byte[] capturedArray = null;
|
||||
|
||||
@Override
|
||||
public int read(byte[] buf, int off, int len) {
|
||||
if (capturedArray != null) {
|
||||
return -1;
|
||||
} else {
|
||||
capturedArray = buf;
|
||||
for (int x = 0; x < len; ++x) {
|
||||
buf[x] = (byte) x;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
// Purposefully do nothing.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// A stream which exposes the byte array passed into write(byte[], int, int).
|
||||
private static class EvilOutputStream extends OutputStream {
|
||||
public byte[] capturedArray = null;
|
||||
|
||||
@Override
|
||||
public void write(byte[] buf, int off, int len) {
|
||||
if (capturedArray == null) {
|
||||
capturedArray = buf;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int ignored) {
|
||||
// Purposefully do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
public void testToStringUtf8() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
byte[] testBytes = testString.getBytes("UTF-8");
|
||||
ByteString byteString = ByteString.copyFrom(testBytes);
|
||||
assertEquals("copyToStringUtf8 must respect the charset",
|
||||
testString, byteString.toStringUtf8());
|
||||
}
|
||||
|
||||
public void testNewOutput_InitialCapacity() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString.Output output = ByteString.newOutput(bytes.length + 100);
|
||||
output.write(bytes);
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue(
|
||||
"String built from newOutput(int) must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
|
||||
// Test newOutput() using a variety of buffer sizes and a variety of (fixed)
|
||||
// write sizes
|
||||
public void testNewOutput_ArrayWrite() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
int length = bytes.length;
|
||||
int[] bufferSizes = {128, 256, length / 2, length - 1, length, length + 1,
|
||||
2 * length, 3 * length};
|
||||
int[] writeSizes = {1, 4, 5, 7, 23, bytes.length};
|
||||
|
||||
for (int bufferSize : bufferSizes) {
|
||||
for (int writeSize : writeSizes) {
|
||||
// Test writing the entire output writeSize bytes at a time.
|
||||
ByteString.Output output = ByteString.newOutput(bufferSize);
|
||||
for (int i = 0; i < length; i += writeSize) {
|
||||
output.write(bytes, i, Math.min(writeSize, length - i));
|
||||
}
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue("String built from newOutput() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test newOutput() using a variety of buffer sizes, but writing all the
|
||||
// characters using write(byte);
|
||||
public void testNewOutput_WriteChar() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
int length = bytes.length;
|
||||
int[] bufferSizes = {0, 1, 128, 256, length / 2,
|
||||
length - 1, length, length + 1,
|
||||
2 * length, 3 * length};
|
||||
for (int bufferSize : bufferSizes) {
|
||||
ByteString.Output output = ByteString.newOutput(bufferSize);
|
||||
for (byte byteValue : bytes) {
|
||||
output.write(byteValue);
|
||||
}
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue("String built from newOutput() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
|
||||
// Test newOutput() in which we write the bytes using a variety of methods
|
||||
// and sizes, and in which we repeatedly call toByteString() in the middle.
|
||||
public void testNewOutput_Mixed() throws IOException {
|
||||
Random rng = new Random(1);
|
||||
byte[] bytes = getTestBytes();
|
||||
int length = bytes.length;
|
||||
int[] bufferSizes = {0, 1, 128, 256, length / 2,
|
||||
length - 1, length, length + 1,
|
||||
2 * length, 3 * length};
|
||||
|
||||
for (int bufferSize : bufferSizes) {
|
||||
// Test writing the entire output using a mixture of write sizes and
|
||||
// methods;
|
||||
ByteString.Output output = ByteString.newOutput(bufferSize);
|
||||
int position = 0;
|
||||
while (position < bytes.length) {
|
||||
if (rng.nextBoolean()) {
|
||||
int count = 1 + rng.nextInt(bytes.length - position);
|
||||
output.write(bytes, position, count);
|
||||
position += count;
|
||||
} else {
|
||||
output.write(bytes[position]);
|
||||
position++;
|
||||
}
|
||||
assertEquals("size() returns the right value", position, output.size());
|
||||
assertTrue("newOutput() substring must have correct bytes",
|
||||
isArrayRange(output.toByteString().toByteArray(),
|
||||
bytes, 0, position));
|
||||
}
|
||||
ByteString byteString = output.toByteString();
|
||||
assertTrue("String built from newOutput() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
|
||||
public void testNewOutputEmpty() throws IOException {
|
||||
// Make sure newOutput() correctly builds empty byte strings
|
||||
ByteString byteString = ByteString.newOutput().toByteString();
|
||||
assertEquals(ByteString.EMPTY, byteString);
|
||||
}
|
||||
|
||||
public void testNewOutput_Mutating() throws IOException {
|
||||
Output os = ByteString.newOutput(5);
|
||||
os.write(new byte[] {1, 2, 3, 4, 5});
|
||||
EvilOutputStream eos = new EvilOutputStream();
|
||||
os.writeTo(eos);
|
||||
byte[] capturedArray = eos.capturedArray;
|
||||
ByteString byteString = os.toByteString();
|
||||
byte[] oldValue = byteString.toByteArray();
|
||||
Arrays.fill(capturedArray, (byte) 0);
|
||||
byte[] newValue = byteString.toByteArray();
|
||||
assertTrue("Output must not provide access to the underlying byte array",
|
||||
Arrays.equals(oldValue, newValue));
|
||||
}
|
||||
|
||||
public void testNewCodedBuilder() throws IOException {
|
||||
byte[] bytes = getTestBytes();
|
||||
ByteString.CodedBuilder builder = ByteString.newCodedBuilder(bytes.length);
|
||||
builder.getCodedOutput().writeRawBytes(bytes);
|
||||
ByteString byteString = builder.build();
|
||||
assertTrue("String built from newCodedBuilder() must contain the expected bytes",
|
||||
isArrayRange(bytes, byteString.toByteArray(), 0, bytes.length));
|
||||
}
|
||||
|
||||
public void testSubstringParity() {
|
||||
byte[] bigBytes = getTestBytes(2048 * 1024, 113344L);
|
||||
int start = 512 * 1024 - 3333;
|
||||
int end = 512 * 1024 + 7777;
|
||||
ByteString concreteSubstring = ByteString.copyFrom(bigBytes).substring(start, end);
|
||||
boolean ok = true;
|
||||
for (int i = start; ok && i < end; ++i) {
|
||||
ok = (bigBytes[i] == concreteSubstring.byteAt(i - start));
|
||||
}
|
||||
assertTrue("Concrete substring didn't capture the right bytes", ok);
|
||||
|
||||
ByteString literalString = ByteString.copyFrom(bigBytes, start, end - start);
|
||||
assertTrue("Substring must be equal to literal string",
|
||||
concreteSubstring.equals(literalString));
|
||||
assertEquals("Substring must have same hashcode as literal string",
|
||||
literalString.hashCode(), concreteSubstring.hashCode());
|
||||
}
|
||||
|
||||
public void testCompositeSubstring() {
|
||||
byte[] referenceBytes = getTestBytes(77748, 113344L);
|
||||
|
||||
List<ByteString> pieces = makeConcretePieces(referenceBytes);
|
||||
ByteString listString = ByteString.copyFrom(pieces);
|
||||
|
||||
int from = 1000;
|
||||
int to = 40000;
|
||||
ByteString compositeSubstring = listString.substring(from, to);
|
||||
byte[] substringBytes = compositeSubstring.toByteArray();
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < to - from; ++i) {
|
||||
stillEqual = referenceBytes[from + i] == substringBytes[i];
|
||||
}
|
||||
assertTrue("Substring must return correct bytes", stillEqual);
|
||||
|
||||
stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < to - from; ++i) {
|
||||
stillEqual = referenceBytes[from + i] == compositeSubstring.byteAt(i);
|
||||
}
|
||||
assertTrue("Substring must support byteAt() correctly", stillEqual);
|
||||
|
||||
ByteString literalSubstring = ByteString.copyFrom(referenceBytes, from, to - from);
|
||||
assertTrue("Composite substring must equal a literal substring over the same bytes",
|
||||
compositeSubstring.equals(literalSubstring));
|
||||
assertTrue("Literal substring must equal a composite substring over the same bytes",
|
||||
literalSubstring.equals(compositeSubstring));
|
||||
|
||||
assertEquals("We must get the same hashcodes for composite and literal substrings",
|
||||
literalSubstring.hashCode(), compositeSubstring.hashCode());
|
||||
|
||||
assertFalse("We can't be equal to a proper substring",
|
||||
compositeSubstring.equals(literalSubstring.substring(0, literalSubstring.size() - 1)));
|
||||
}
|
||||
|
||||
public void testCopyFromList() {
|
||||
byte[] referenceBytes = getTestBytes(77748, 113344L);
|
||||
ByteString literalString = ByteString.copyFrom(referenceBytes);
|
||||
|
||||
List<ByteString> pieces = makeConcretePieces(referenceBytes);
|
||||
ByteString listString = ByteString.copyFrom(pieces);
|
||||
|
||||
assertTrue("Composite string must be equal to literal string",
|
||||
listString.equals(literalString));
|
||||
assertEquals("Composite string must have same hashcode as literal string",
|
||||
literalString.hashCode(), listString.hashCode());
|
||||
}
|
||||
|
||||
public void testConcat() {
|
||||
byte[] referenceBytes = getTestBytes(77748, 113344L);
|
||||
ByteString literalString = ByteString.copyFrom(referenceBytes);
|
||||
|
||||
List<ByteString> pieces = makeConcretePieces(referenceBytes);
|
||||
|
||||
Iterator<ByteString> iter = pieces.iterator();
|
||||
ByteString concatenatedString = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
concatenatedString = concatenatedString.concat(iter.next());
|
||||
}
|
||||
|
||||
assertTrue("Concatenated string must be equal to literal string",
|
||||
concatenatedString.equals(literalString));
|
||||
assertEquals("Concatenated string must have same hashcode as literal string",
|
||||
literalString.hashCode(), concatenatedString.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Rope implementation can deal with Empty nodes, even though we
|
||||
* guard against them. See also {@link LiteralByteStringTest#testConcat_empty()}.
|
||||
*/
|
||||
public void testConcat_empty() {
|
||||
byte[] referenceBytes = getTestBytes(7748, 113344L);
|
||||
ByteString literalString = ByteString.copyFrom(referenceBytes);
|
||||
|
||||
ByteString duo = RopeByteString.newInstanceForTest(literalString, literalString);
|
||||
ByteString temp = RopeByteString.newInstanceForTest(
|
||||
RopeByteString.newInstanceForTest(literalString, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, literalString));
|
||||
ByteString quintet = RopeByteString.newInstanceForTest(temp, ByteString.EMPTY);
|
||||
|
||||
assertTrue("String with concatenated nulls must equal simple concatenate",
|
||||
duo.equals(quintet));
|
||||
assertEquals("String with concatenated nulls have same hashcode as simple concatenate",
|
||||
duo.hashCode(), quintet.hashCode());
|
||||
|
||||
ByteString.ByteIterator duoIter = duo.iterator();
|
||||
ByteString.ByteIterator quintetIter = quintet.iterator();
|
||||
boolean stillEqual = true;
|
||||
while (stillEqual && quintetIter.hasNext()) {
|
||||
stillEqual = (duoIter.nextByte() == quintetIter.nextByte());
|
||||
}
|
||||
assertTrue("We must get the same characters by iterating", stillEqual);
|
||||
assertFalse("Iterator must be exhausted", duoIter.hasNext());
|
||||
try {
|
||||
duoIter.nextByte();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NoSuchElementException e) {
|
||||
// This is success
|
||||
}
|
||||
try {
|
||||
quintetIter.nextByte();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NoSuchElementException e) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
// Test that even if we force empty strings in as rope leaves in this
|
||||
// configuration, we always get a (possibly Bounded) LiteralByteString
|
||||
// for a length 1 substring.
|
||||
//
|
||||
// It is possible, using the testing factory method to create deeply nested
|
||||
// trees of empty leaves, to make a string that will fail this test.
|
||||
for (int i = 1; i < duo.size(); ++i) {
|
||||
assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
|
||||
duo.substring(i - 1, i) instanceof LiteralByteString);
|
||||
}
|
||||
for (int i = 1; i < quintet.size(); ++i) {
|
||||
assertTrue("Substrings of size() < 2 must not be RopeByteStrings",
|
||||
quintet.substring(i - 1, i) instanceof LiteralByteString);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartsWith() {
|
||||
byte[] bytes = getTestBytes(1000, 1234L);
|
||||
ByteString string = ByteString.copyFrom(bytes);
|
||||
ByteString prefix = ByteString.copyFrom(bytes, 0, 500);
|
||||
ByteString suffix = ByteString.copyFrom(bytes, 400, 600);
|
||||
assertTrue(string.startsWith(ByteString.EMPTY));
|
||||
assertTrue(string.startsWith(string));
|
||||
assertTrue(string.startsWith(prefix));
|
||||
assertFalse(string.startsWith(suffix));
|
||||
assertFalse(prefix.startsWith(suffix));
|
||||
assertFalse(suffix.startsWith(prefix));
|
||||
assertFalse(ByteString.EMPTY.startsWith(prefix));
|
||||
assertTrue(ByteString.EMPTY.startsWith(ByteString.EMPTY));
|
||||
}
|
||||
|
||||
static List<ByteString> makeConcretePieces(byte[] referenceBytes) {
|
||||
List<ByteString> pieces = new ArrayList<ByteString>();
|
||||
// Starting length should be small enough that we'll do some concatenating by
|
||||
// copying if we just concatenate all these pieces together.
|
||||
for (int start = 0, length = 16; start < referenceBytes.length; start += length) {
|
||||
length = (length << 1) - 1;
|
||||
if (start + length > referenceBytes.length) {
|
||||
length = referenceBytes.length - start;
|
||||
}
|
||||
pieces.add(ByteString.copyFrom(referenceBytes, start, length));
|
||||
}
|
||||
return pieces;
|
||||
}
|
||||
}
|
||||
528
java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
Normal file
528
java/src/test/java/com/google/protobuf/CodedInputStreamTest.java
Normal file
@@ -0,0 +1,528 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestRecursiveMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link CodedInputStream}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class CodedInputStreamTest extends TestCase {
|
||||
/**
|
||||
* Helper to construct a byte array from a bunch of bytes. The inputs are
|
||||
* actually ints so that I can use hex notation and not get stupid errors
|
||||
* about precision.
|
||||
*/
|
||||
private byte[] bytes(int... bytesAsInts) {
|
||||
byte[] bytes = new byte[bytesAsInts.length];
|
||||
for (int i = 0; i < bytesAsInts.length; i++) {
|
||||
bytes[i] = (byte) bytesAsInts[i];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* An InputStream which limits the number of bytes it reads at a time.
|
||||
* We use this to make sure that CodedInputStream doesn't screw up when
|
||||
* reading in small blocks.
|
||||
*/
|
||||
private static final class SmallBlockInputStream extends FilterInputStream {
|
||||
private final int blockSize;
|
||||
|
||||
public SmallBlockInputStream(byte[] data, int blockSize) {
|
||||
this(new ByteArrayInputStream(data), blockSize);
|
||||
}
|
||||
|
||||
public SmallBlockInputStream(InputStream in, int blockSize) {
|
||||
super(in);
|
||||
this.blockSize = blockSize;
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return super.read(b, 0, Math.min(b.length, blockSize));
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return super.read(b, off, Math.min(len, blockSize));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawVarint32() and readRawVarint64() and
|
||||
* checks that the result matches the given value.
|
||||
*/
|
||||
private void assertReadVarint(byte[] data, long value) throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
assertEquals((int)value, input.readRawVarint32());
|
||||
|
||||
input = CodedInputStream.newInstance(data);
|
||||
assertEquals(value, input.readRawVarint64());
|
||||
assertTrue(input.isAtEnd());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals((int)value, input.readRawVarint32());
|
||||
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals(value, input.readRawVarint64());
|
||||
assertTrue(input.isAtEnd());
|
||||
}
|
||||
|
||||
// Try reading direct from an InputStream. We want to verify that it
|
||||
// doesn't read past the end of the input, so we copy to a new, bigger
|
||||
// array first.
|
||||
byte[] longerData = new byte[data.length + 1];
|
||||
System.arraycopy(data, 0, longerData, 0, data.length);
|
||||
InputStream rawInput = new ByteArrayInputStream(longerData);
|
||||
assertEquals((int)value, CodedInputStream.readRawVarint32(rawInput));
|
||||
assertEquals(1, rawInput.available());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawVarint32() and readRawVarint64() and
|
||||
* expects them to fail with an InvalidProtocolBufferException whose
|
||||
* description matches the given one.
|
||||
*/
|
||||
private void assertReadVarintFailure(
|
||||
InvalidProtocolBufferException expected, byte[] data)
|
||||
throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
try {
|
||||
input.readRawVarint32();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(expected.getMessage(), e.getMessage());
|
||||
}
|
||||
|
||||
input = CodedInputStream.newInstance(data);
|
||||
try {
|
||||
input.readRawVarint64();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(expected.getMessage(), e.getMessage());
|
||||
}
|
||||
|
||||
// Make sure we get the same error when reading direct from an InputStream.
|
||||
try {
|
||||
CodedInputStream.readRawVarint32(new ByteArrayInputStream(data));
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(expected.getMessage(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests readRawVarint32() and readRawVarint64(). */
|
||||
public void testReadVarint() throws Exception {
|
||||
assertReadVarint(bytes(0x00), 0);
|
||||
assertReadVarint(bytes(0x01), 1);
|
||||
assertReadVarint(bytes(0x7f), 127);
|
||||
// 14882
|
||||
assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
|
||||
// 2961488830
|
||||
assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x0bL << 28));
|
||||
|
||||
// 64-bit
|
||||
// 7256456126
|
||||
assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x1bL << 28));
|
||||
// 41256202580718336
|
||||
assertReadVarint(
|
||||
bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
|
||||
(0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
|
||||
(0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
|
||||
// 11964378330978735131
|
||||
assertReadVarint(
|
||||
bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
|
||||
(0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
|
||||
(0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
|
||||
(0x05L << 49) | (0x26L << 56) | (0x01L << 63));
|
||||
|
||||
// Failures
|
||||
assertReadVarintFailure(
|
||||
InvalidProtocolBufferException.malformedVarint(),
|
||||
bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
0x00));
|
||||
assertReadVarintFailure(
|
||||
InvalidProtocolBufferException.truncatedMessage(),
|
||||
bytes(0x80));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawLittleEndian32() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertReadLittleEndian32(byte[] data, int value)
|
||||
throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
assertEquals(value, input.readRawLittleEndian32());
|
||||
assertTrue(input.isAtEnd());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals(value, input.readRawLittleEndian32());
|
||||
assertTrue(input.isAtEnd());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using readRawLittleEndian64() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertReadLittleEndian64(byte[] data, long value)
|
||||
throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
assertEquals(value, input.readRawLittleEndian64());
|
||||
assertTrue(input.isAtEnd());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(data, blockSize));
|
||||
assertEquals(value, input.readRawLittleEndian64());
|
||||
assertTrue(input.isAtEnd());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests readRawLittleEndian32() and readRawLittleEndian64(). */
|
||||
public void testReadLittleEndian() throws Exception {
|
||||
assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
|
||||
assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
|
||||
|
||||
assertReadLittleEndian64(
|
||||
bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
|
||||
0x123456789abcdef0L);
|
||||
assertReadLittleEndian64(
|
||||
bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
|
||||
0x9abcdef012345678L);
|
||||
}
|
||||
|
||||
/** Test decodeZigZag32() and decodeZigZag64(). */
|
||||
public void testDecodeZigZag() throws Exception {
|
||||
assertEquals( 0, CodedInputStream.decodeZigZag32(0));
|
||||
assertEquals(-1, CodedInputStream.decodeZigZag32(1));
|
||||
assertEquals( 1, CodedInputStream.decodeZigZag32(2));
|
||||
assertEquals(-2, CodedInputStream.decodeZigZag32(3));
|
||||
assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE));
|
||||
assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF));
|
||||
assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE));
|
||||
assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF));
|
||||
|
||||
assertEquals( 0, CodedInputStream.decodeZigZag64(0));
|
||||
assertEquals(-1, CodedInputStream.decodeZigZag64(1));
|
||||
assertEquals( 1, CodedInputStream.decodeZigZag64(2));
|
||||
assertEquals(-2, CodedInputStream.decodeZigZag64(3));
|
||||
assertEquals(0x000000003FFFFFFFL,
|
||||
CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
|
||||
assertEquals(0xFFFFFFFFC0000000L,
|
||||
CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
|
||||
assertEquals(0x000000007FFFFFFFL,
|
||||
CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
|
||||
assertEquals(0xFFFFFFFF80000000L,
|
||||
CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
|
||||
assertEquals(0x7FFFFFFFFFFFFFFFL,
|
||||
CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
|
||||
assertEquals(0x8000000000000000L,
|
||||
CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
|
||||
}
|
||||
|
||||
/** Tests reading and parsing a whole message with every field type. */
|
||||
public void testReadWholeMessage() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
assertEquals(rawBytes.length, message.getSerializedSize());
|
||||
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
|
||||
message2 = TestAllTypes.parseFrom(
|
||||
new SmallBlockInputStream(rawBytes, blockSize));
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests skipField(). */
|
||||
public void testSkipWholeMessage() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
|
||||
// Create two parallel inputs. Parse one as unknown fields while using
|
||||
// skipField() to skip each field on the other. Expect the same tags.
|
||||
CodedInputStream input1 = CodedInputStream.newInstance(rawBytes);
|
||||
CodedInputStream input2 = CodedInputStream.newInstance(rawBytes);
|
||||
UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
|
||||
|
||||
while (true) {
|
||||
int tag = input1.readTag();
|
||||
assertEquals(tag, input2.readTag());
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
unknownFields.mergeFieldFrom(tag, input1);
|
||||
input2.skipField(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a bug in skipRawBytes() has been fixed: if the skip skips
|
||||
* exactly up to a limit, this should not break things.
|
||||
*/
|
||||
public void testSkipRawBytesBug() throws Exception {
|
||||
byte[] rawBytes = new byte[] { 1, 2 };
|
||||
CodedInputStream input = CodedInputStream.newInstance(rawBytes);
|
||||
|
||||
int limit = input.pushLimit(1);
|
||||
input.skipRawBytes(1);
|
||||
input.popLimit(limit);
|
||||
assertEquals(2, input.readRawByte());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a bug in skipRawBytes() has been fixed: if the skip skips
|
||||
* past the end of a buffer with a limit that has been set past the end of
|
||||
* that buffer, this should not break things.
|
||||
*/
|
||||
public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
|
||||
byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 };
|
||||
CodedInputStream input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(rawBytes, 3));
|
||||
|
||||
int limit = input.pushLimit(4);
|
||||
// In order to expose the bug we need to read at least one byte to prime the
|
||||
// buffer inside the CodedInputStream.
|
||||
assertEquals(1, input.readRawByte());
|
||||
// Skip to the end of the limit.
|
||||
input.skipRawBytes(3);
|
||||
assertTrue(input.isAtEnd());
|
||||
input.popLimit(limit);
|
||||
assertEquals(5, input.readRawByte());
|
||||
}
|
||||
|
||||
public void testReadHugeBlob() throws Exception {
|
||||
// Allocate and initialize a 1MB blob.
|
||||
byte[] blob = new byte[1 << 20];
|
||||
for (int i = 0; i < blob.length; i++) {
|
||||
blob[i] = (byte)i;
|
||||
}
|
||||
|
||||
// Make a message containing it.
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
builder.setOptionalBytes(ByteString.copyFrom(blob));
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
// Serialize and parse it. Make sure to parse from an InputStream, not
|
||||
// directly from a ByteString, so that CodedInputStream uses buffered
|
||||
// reading.
|
||||
TestAllTypes message2 =
|
||||
TestAllTypes.parseFrom(message.toByteString().newInput());
|
||||
|
||||
assertEquals(message.getOptionalBytes(), message2.getOptionalBytes());
|
||||
|
||||
// Make sure all the other fields were parsed correctly.
|
||||
TestAllTypes message3 = TestAllTypes.newBuilder(message2)
|
||||
.setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
|
||||
.build();
|
||||
TestUtil.assertAllFieldsSet(message3);
|
||||
}
|
||||
|
||||
public void testReadMaliciouslyLargeBlob() throws Exception {
|
||||
ByteString.Output rawOutput = ByteString.newOutput();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
|
||||
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
output.writeRawVarint32(tag);
|
||||
output.writeRawVarint32(0x7FFFFFFF);
|
||||
output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
|
||||
output.flush();
|
||||
|
||||
CodedInputStream input = rawOutput.toByteString().newCodedInput();
|
||||
assertEquals(tag, input.readTag());
|
||||
|
||||
try {
|
||||
input.readBytes();
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
private TestRecursiveMessage makeRecursiveMessage(int depth) {
|
||||
if (depth == 0) {
|
||||
return TestRecursiveMessage.newBuilder().setI(5).build();
|
||||
} else {
|
||||
return TestRecursiveMessage.newBuilder()
|
||||
.setA(makeRecursiveMessage(depth - 1)).build();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMessageDepth(TestRecursiveMessage message, int depth) {
|
||||
if (depth == 0) {
|
||||
assertFalse(message.hasA());
|
||||
assertEquals(5, message.getI());
|
||||
} else {
|
||||
assertTrue(message.hasA());
|
||||
assertMessageDepth(message.getA(), depth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMaliciousRecursion() throws Exception {
|
||||
ByteString data64 = makeRecursiveMessage(64).toByteString();
|
||||
ByteString data65 = makeRecursiveMessage(65).toByteString();
|
||||
|
||||
assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64);
|
||||
|
||||
try {
|
||||
TestRecursiveMessage.parseFrom(data65);
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
|
||||
CodedInputStream input = data64.newCodedInput();
|
||||
input.setRecursionLimit(8);
|
||||
try {
|
||||
TestRecursiveMessage.parseFrom(input);
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
public void testSizeLimit() throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(
|
||||
TestUtil.getAllSet().toByteString().newInput());
|
||||
input.setSizeLimit(16);
|
||||
|
||||
try {
|
||||
TestAllTypes.parseFrom(input);
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
public void testResetSizeCounter() throws Exception {
|
||||
CodedInputStream input = CodedInputStream.newInstance(
|
||||
new SmallBlockInputStream(new byte[256], 8));
|
||||
input.setSizeLimit(16);
|
||||
input.readRawBytes(16);
|
||||
assertEquals(16, input.getTotalBytesRead());
|
||||
|
||||
try {
|
||||
input.readRawByte();
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
|
||||
input.resetSizeCounter();
|
||||
assertEquals(0, input.getTotalBytesRead());
|
||||
input.readRawByte(); // No exception thrown.
|
||||
input.resetSizeCounter();
|
||||
assertEquals(0, input.getTotalBytesRead());
|
||||
|
||||
try {
|
||||
input.readRawBytes(16); // Hits limit again.
|
||||
fail("Should have thrown an exception!");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// success.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that if we read an string that contains invalid UTF-8, no exception
|
||||
* is thrown. Instead, the invalid bytes are replaced with the Unicode
|
||||
* "replacement character" U+FFFD.
|
||||
*/
|
||||
public void testReadInvalidUtf8() throws Exception {
|
||||
ByteString.Output rawOutput = ByteString.newOutput();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
|
||||
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
output.writeRawVarint32(tag);
|
||||
output.writeRawVarint32(1);
|
||||
output.writeRawBytes(new byte[] { (byte)0x80 });
|
||||
output.flush();
|
||||
|
||||
CodedInputStream input = rawOutput.toByteString().newCodedInput();
|
||||
assertEquals(tag, input.readTag());
|
||||
String text = input.readString();
|
||||
assertEquals(0xfffd, text.charAt(0));
|
||||
}
|
||||
|
||||
public void testReadFromSlice() throws Exception {
|
||||
byte[] bytes = bytes(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
CodedInputStream in = CodedInputStream.newInstance(bytes, 3, 5);
|
||||
assertEquals(0, in.getTotalBytesRead());
|
||||
for (int i = 3; i < 8; i++) {
|
||||
assertEquals(i, in.readRawByte());
|
||||
assertEquals(i-2, in.getTotalBytesRead());
|
||||
}
|
||||
// eof
|
||||
assertEquals(0, in.readTag());
|
||||
assertEquals(5, in.getTotalBytesRead());
|
||||
}
|
||||
|
||||
public void testInvalidTag() throws Exception {
|
||||
// Any tag number which corresponds to field number zero is invalid and
|
||||
// should throw InvalidProtocolBufferException.
|
||||
for (int i = 0; i < 8; i++) {
|
||||
try {
|
||||
CodedInputStream.newInstance(bytes(i)).readTag();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(InvalidProtocolBufferException.invalidTag().getMessage(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.SparseEnumMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestSparseEnum;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link CodedOutputStream}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class CodedOutputStreamTest extends TestCase {
|
||||
/**
|
||||
* Helper to construct a byte array from a bunch of bytes. The inputs are
|
||||
* actually ints so that I can use hex notation and not get stupid errors
|
||||
* about precision.
|
||||
*/
|
||||
private byte[] bytes(int... bytesAsInts) {
|
||||
byte[] bytes = new byte[bytesAsInts.length];
|
||||
for (int i = 0; i < bytesAsInts.length; i++) {
|
||||
bytes[i] = (byte) bytesAsInts[i];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/** Arrays.asList() does not work with arrays of primitives. :( */
|
||||
private List<Byte> toList(byte[] bytes) {
|
||||
List<Byte> result = new ArrayList<Byte>();
|
||||
for (byte b : bytes) {
|
||||
result.add(b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void assertEqualBytes(byte[] a, byte[] b) {
|
||||
assertEquals(toList(a), toList(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given value using writeRawVarint32() and writeRawVarint64() and
|
||||
* checks that the result matches the given bytes.
|
||||
*/
|
||||
private void assertWriteVarint(byte[] data, long value) throws Exception {
|
||||
// Only do 32-bit write if the value fits in 32 bits.
|
||||
if ((value >>> 32) == 0) {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawVarint32((int) value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Also try computing size.
|
||||
assertEquals(data.length,
|
||||
CodedOutputStream.computeRawVarint32Size((int) value));
|
||||
}
|
||||
|
||||
{
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawVarint64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Also try computing size.
|
||||
assertEquals(data.length,
|
||||
CodedOutputStream.computeRawVarint64Size(value));
|
||||
}
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
// Only do 32-bit write if the value fits in 32 bits.
|
||||
if ((value >>> 32) == 0) {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output =
|
||||
CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawVarint32((int) value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
|
||||
{
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output =
|
||||
CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawVarint64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests writeRawVarint32() and writeRawVarint64(). */
|
||||
public void testWriteVarint() throws Exception {
|
||||
assertWriteVarint(bytes(0x00), 0);
|
||||
assertWriteVarint(bytes(0x01), 1);
|
||||
assertWriteVarint(bytes(0x7f), 127);
|
||||
// 14882
|
||||
assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
|
||||
// 2961488830
|
||||
assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x0bL << 28));
|
||||
|
||||
// 64-bit
|
||||
// 7256456126
|
||||
assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
|
||||
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
|
||||
(0x1bL << 28));
|
||||
// 41256202580718336
|
||||
assertWriteVarint(
|
||||
bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
|
||||
(0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
|
||||
(0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
|
||||
// 11964378330978735131
|
||||
assertWriteVarint(
|
||||
bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
|
||||
(0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
|
||||
(0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
|
||||
(0x05L << 49) | (0x26L << 56) | (0x01L << 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using writeRawLittleEndian32() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertWriteLittleEndian32(byte[] data, int value)
|
||||
throws Exception {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawLittleEndian32(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
rawOutput = new ByteArrayOutputStream();
|
||||
output = CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawLittleEndian32(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given bytes using writeRawLittleEndian64() and checks
|
||||
* that the result matches the given value.
|
||||
*/
|
||||
private void assertWriteLittleEndian64(byte[] data, long value)
|
||||
throws Exception {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
|
||||
output.writeRawLittleEndian64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
|
||||
rawOutput = new ByteArrayOutputStream();
|
||||
output = CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
output.writeRawLittleEndian64(value);
|
||||
output.flush();
|
||||
assertEqualBytes(data, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */
|
||||
public void testWriteLittleEndian() throws Exception {
|
||||
assertWriteLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
|
||||
assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
|
||||
|
||||
assertWriteLittleEndian64(
|
||||
bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
|
||||
0x123456789abcdef0L);
|
||||
assertWriteLittleEndian64(
|
||||
bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
|
||||
0x9abcdef012345678L);
|
||||
}
|
||||
|
||||
/** Test encodeZigZag32() and encodeZigZag64(). */
|
||||
public void testEncodeZigZag() throws Exception {
|
||||
assertEquals(0, CodedOutputStream.encodeZigZag32( 0));
|
||||
assertEquals(1, CodedOutputStream.encodeZigZag32(-1));
|
||||
assertEquals(2, CodedOutputStream.encodeZigZag32( 1));
|
||||
assertEquals(3, CodedOutputStream.encodeZigZag32(-2));
|
||||
assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF));
|
||||
assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000));
|
||||
assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF));
|
||||
assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000));
|
||||
|
||||
assertEquals(0, CodedOutputStream.encodeZigZag64( 0));
|
||||
assertEquals(1, CodedOutputStream.encodeZigZag64(-1));
|
||||
assertEquals(2, CodedOutputStream.encodeZigZag64( 1));
|
||||
assertEquals(3, CodedOutputStream.encodeZigZag64(-2));
|
||||
assertEquals(0x000000007FFFFFFEL,
|
||||
CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL));
|
||||
assertEquals(0x000000007FFFFFFFL,
|
||||
CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L));
|
||||
assertEquals(0x00000000FFFFFFFEL,
|
||||
CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL));
|
||||
assertEquals(0x00000000FFFFFFFFL,
|
||||
CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L));
|
||||
assertEquals(0xFFFFFFFFFFFFFFFEL,
|
||||
CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL));
|
||||
assertEquals(0xFFFFFFFFFFFFFFFFL,
|
||||
CodedOutputStream.encodeZigZag64(0x8000000000000000L));
|
||||
|
||||
// Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
|
||||
// were chosen semi-randomly via keyboard bashing.
|
||||
assertEquals(0,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0)));
|
||||
assertEquals(1,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1)));
|
||||
assertEquals(-1,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1)));
|
||||
assertEquals(14927,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927)));
|
||||
assertEquals(-3612,
|
||||
CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612)));
|
||||
|
||||
assertEquals(0,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0)));
|
||||
assertEquals(1,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1)));
|
||||
assertEquals(-1,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1)));
|
||||
assertEquals(14927,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927)));
|
||||
assertEquals(-3612,
|
||||
CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612)));
|
||||
|
||||
assertEquals(856912304801416L,
|
||||
CodedOutputStream.encodeZigZag64(
|
||||
CodedInputStream.decodeZigZag64(
|
||||
856912304801416L)));
|
||||
assertEquals(-75123905439571256L,
|
||||
CodedOutputStream.encodeZigZag64(
|
||||
CodedInputStream.decodeZigZag64(
|
||||
-75123905439571256L)));
|
||||
}
|
||||
|
||||
/** Tests writing a whole message with every field type. */
|
||||
public void testWriteWholeMessage() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes);
|
||||
|
||||
// Try different block sizes.
|
||||
for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
|
||||
ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
|
||||
CodedOutputStream output =
|
||||
CodedOutputStream.newInstance(rawOutput, blockSize);
|
||||
message.writeTo(output);
|
||||
output.flush();
|
||||
assertEqualBytes(rawBytes, rawOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests writing a whole message with every packed field type. Ensures the
|
||||
* wire format of packed fields is compatible with C++. */
|
||||
public void testWriteWholePackedFieldsMessage() throws Exception {
|
||||
TestPackedTypes message = TestUtil.getPackedSet();
|
||||
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
assertEqualBytes(TestUtil.getGoldenPackedFieldsMessage().toByteArray(),
|
||||
rawBytes);
|
||||
}
|
||||
|
||||
/** Test writing a message containing a negative enum value. This used to
|
||||
* fail because the size was not properly computed as a sign-extended varint.
|
||||
*/
|
||||
public void testWriteMessageWithNegativeEnumValue() throws Exception {
|
||||
SparseEnumMessage message = SparseEnumMessage.newBuilder()
|
||||
.setSparseEnum(TestSparseEnum.SPARSE_E) .build();
|
||||
assertTrue(message.getSparseEnum().getNumber() < 0);
|
||||
byte[] rawBytes = message.toByteArray();
|
||||
SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes);
|
||||
assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestDeprecatedFields;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
/**
|
||||
* Test field deprecation
|
||||
*
|
||||
* @author birdo@google.com (Roberto Scaramuzzi)
|
||||
*/
|
||||
public class DeprecatedFieldTest extends TestCase {
|
||||
private String[] deprecatedGetterNames = {
|
||||
"hasDeprecatedInt32",
|
||||
"getDeprecatedInt32"};
|
||||
|
||||
private String[] deprecatedBuilderGetterNames = {
|
||||
"hasDeprecatedInt32",
|
||||
"getDeprecatedInt32",
|
||||
"clearDeprecatedInt32"};
|
||||
|
||||
private String[] deprecatedBuilderSetterNames = {
|
||||
"setDeprecatedInt32"};
|
||||
|
||||
public void testDeprecatedField() throws Exception {
|
||||
Class<?> deprecatedFields = TestDeprecatedFields.class;
|
||||
Class<?> deprecatedFieldsBuilder = TestDeprecatedFields.Builder.class;
|
||||
for (String name : deprecatedGetterNames) {
|
||||
Method method = deprecatedFields.getMethod(name);
|
||||
assertTrue("Method " + name + " should be deprecated",
|
||||
isDeprecated(method));
|
||||
}
|
||||
for (String name : deprecatedBuilderGetterNames) {
|
||||
Method method = deprecatedFieldsBuilder.getMethod(name);
|
||||
assertTrue("Method " + name + " should be deprecated",
|
||||
isDeprecated(method));
|
||||
}
|
||||
for (String name : deprecatedBuilderSetterNames) {
|
||||
Method method = deprecatedFieldsBuilder.getMethod(name, int.class);
|
||||
assertTrue("Method " + name + " should be deprecated",
|
||||
isDeprecated(method));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDeprecated(AnnotatedElement annotated) {
|
||||
return annotated.isAnnotationPresent(Deprecated.class);
|
||||
}
|
||||
}
|
||||
648
java/src/test/java/com/google/protobuf/DescriptorsTest.java
Normal file
648
java/src/test/java/com/google/protobuf/DescriptorsTest.java
Normal file
@@ -0,0 +1,648 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.DescriptorProtos.DescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
|
||||
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
|
||||
import com.google.protobuf.Descriptors.DescriptorValidationException;
|
||||
import com.google.protobuf.Descriptors.FileDescriptor;
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.Descriptors.EnumDescriptor;
|
||||
import com.google.protobuf.Descriptors.EnumValueDescriptor;
|
||||
import com.google.protobuf.Descriptors.ServiceDescriptor;
|
||||
import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
|
||||
import com.google.protobuf.test.UnittestImport;
|
||||
import com.google.protobuf.test.UnittestImport.ImportEnum;
|
||||
import com.google.protobuf.test.UnittestImport.ImportMessage;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignEnum;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
import protobuf_unittest.UnittestCustomOptions;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Descriptors}.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class DescriptorsTest extends TestCase {
|
||||
|
||||
// Regression test for bug where referencing a FieldDescriptor.Type value
|
||||
// before a FieldDescriptorProto.Type value would yield a
|
||||
// ExceptionInInitializerError.
|
||||
@SuppressWarnings("unused")
|
||||
private static final Object STATIC_INIT_TEST = FieldDescriptor.Type.BOOL;
|
||||
|
||||
public void testFieldTypeEnumMapping() throws Exception {
|
||||
assertEquals(FieldDescriptor.Type.values().length,
|
||||
FieldDescriptorProto.Type.values().length);
|
||||
for (FieldDescriptor.Type type : FieldDescriptor.Type.values()) {
|
||||
FieldDescriptorProto.Type protoType = type.toProto();
|
||||
assertEquals("TYPE_" + type.name(), protoType.name());
|
||||
assertEquals(type, FieldDescriptor.Type.valueOf(protoType));
|
||||
}
|
||||
}
|
||||
|
||||
public void testFileDescriptor() throws Exception {
|
||||
FileDescriptor file = UnittestProto.getDescriptor();
|
||||
|
||||
assertEquals("google/protobuf/unittest.proto", file.getName());
|
||||
assertEquals("protobuf_unittest", file.getPackage());
|
||||
|
||||
assertEquals("UnittestProto", file.getOptions().getJavaOuterClassname());
|
||||
assertEquals("google/protobuf/unittest.proto",
|
||||
file.toProto().getName());
|
||||
|
||||
assertEquals(Arrays.asList(UnittestImport.getDescriptor()),
|
||||
file.getDependencies());
|
||||
|
||||
Descriptor messageType = TestAllTypes.getDescriptor();
|
||||
assertEquals(messageType, file.getMessageTypes().get(0));
|
||||
assertEquals(messageType, file.findMessageTypeByName("TestAllTypes"));
|
||||
assertNull(file.findMessageTypeByName("NoSuchType"));
|
||||
assertNull(file.findMessageTypeByName("protobuf_unittest.TestAllTypes"));
|
||||
for (int i = 0; i < file.getMessageTypes().size(); i++) {
|
||||
assertEquals(i, file.getMessageTypes().get(i).getIndex());
|
||||
}
|
||||
|
||||
EnumDescriptor enumType = ForeignEnum.getDescriptor();
|
||||
assertEquals(enumType, file.getEnumTypes().get(0));
|
||||
assertEquals(enumType, file.findEnumTypeByName("ForeignEnum"));
|
||||
assertNull(file.findEnumTypeByName("NoSuchType"));
|
||||
assertNull(file.findEnumTypeByName("protobuf_unittest.ForeignEnum"));
|
||||
assertEquals(Arrays.asList(ImportEnum.getDescriptor()),
|
||||
UnittestImport.getDescriptor().getEnumTypes());
|
||||
for (int i = 0; i < file.getEnumTypes().size(); i++) {
|
||||
assertEquals(i, file.getEnumTypes().get(i).getIndex());
|
||||
}
|
||||
|
||||
ServiceDescriptor service = TestService.getDescriptor();
|
||||
assertEquals(service, file.getServices().get(0));
|
||||
assertEquals(service, file.findServiceByName("TestService"));
|
||||
assertNull(file.findServiceByName("NoSuchType"));
|
||||
assertNull(file.findServiceByName("protobuf_unittest.TestService"));
|
||||
assertEquals(Collections.emptyList(),
|
||||
UnittestImport.getDescriptor().getServices());
|
||||
for (int i = 0; i < file.getServices().size(); i++) {
|
||||
assertEquals(i, file.getServices().get(i).getIndex());
|
||||
}
|
||||
|
||||
FieldDescriptor extension =
|
||||
UnittestProto.optionalInt32Extension.getDescriptor();
|
||||
assertEquals(extension, file.getExtensions().get(0));
|
||||
assertEquals(extension,
|
||||
file.findExtensionByName("optional_int32_extension"));
|
||||
assertNull(file.findExtensionByName("no_such_ext"));
|
||||
assertNull(file.findExtensionByName(
|
||||
"protobuf_unittest.optional_int32_extension"));
|
||||
assertEquals(Collections.emptyList(),
|
||||
UnittestImport.getDescriptor().getExtensions());
|
||||
for (int i = 0; i < file.getExtensions().size(); i++) {
|
||||
assertEquals(i, file.getExtensions().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDescriptor() throws Exception {
|
||||
Descriptor messageType = TestAllTypes.getDescriptor();
|
||||
Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor();
|
||||
|
||||
assertEquals("TestAllTypes", messageType.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes", messageType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), messageType.getFile());
|
||||
assertNull(messageType.getContainingType());
|
||||
assertEquals(DescriptorProtos.MessageOptions.getDefaultInstance(),
|
||||
messageType.getOptions());
|
||||
assertEquals("TestAllTypes", messageType.toProto().getName());
|
||||
|
||||
assertEquals("NestedMessage", nestedType.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes.NestedMessage",
|
||||
nestedType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), nestedType.getFile());
|
||||
assertEquals(messageType, nestedType.getContainingType());
|
||||
|
||||
FieldDescriptor field = messageType.getFields().get(0);
|
||||
assertEquals("optional_int32", field.getName());
|
||||
assertEquals(field, messageType.findFieldByName("optional_int32"));
|
||||
assertNull(messageType.findFieldByName("no_such_field"));
|
||||
assertEquals(field, messageType.findFieldByNumber(1));
|
||||
assertNull(messageType.findFieldByNumber(571283));
|
||||
for (int i = 0; i < messageType.getFields().size(); i++) {
|
||||
assertEquals(i, messageType.getFields().get(i).getIndex());
|
||||
}
|
||||
|
||||
assertEquals(nestedType, messageType.getNestedTypes().get(0));
|
||||
assertEquals(nestedType, messageType.findNestedTypeByName("NestedMessage"));
|
||||
assertNull(messageType.findNestedTypeByName("NoSuchType"));
|
||||
for (int i = 0; i < messageType.getNestedTypes().size(); i++) {
|
||||
assertEquals(i, messageType.getNestedTypes().get(i).getIndex());
|
||||
}
|
||||
|
||||
EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
|
||||
assertEquals(enumType, messageType.getEnumTypes().get(0));
|
||||
assertEquals(enumType, messageType.findEnumTypeByName("NestedEnum"));
|
||||
assertNull(messageType.findEnumTypeByName("NoSuchType"));
|
||||
for (int i = 0; i < messageType.getEnumTypes().size(); i++) {
|
||||
assertEquals(i, messageType.getEnumTypes().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void testFieldDescriptor() throws Exception {
|
||||
Descriptor messageType = TestAllTypes.getDescriptor();
|
||||
FieldDescriptor primitiveField =
|
||||
messageType.findFieldByName("optional_int32");
|
||||
FieldDescriptor enumField =
|
||||
messageType.findFieldByName("optional_nested_enum");
|
||||
FieldDescriptor messageField =
|
||||
messageType.findFieldByName("optional_foreign_message");
|
||||
FieldDescriptor cordField =
|
||||
messageType.findFieldByName("optional_cord");
|
||||
FieldDescriptor extension =
|
||||
UnittestProto.optionalInt32Extension.getDescriptor();
|
||||
FieldDescriptor nestedExtension = TestRequired.single.getDescriptor();
|
||||
|
||||
assertEquals("optional_int32", primitiveField.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes.optional_int32",
|
||||
primitiveField.getFullName());
|
||||
assertEquals(1, primitiveField.getNumber());
|
||||
assertEquals(messageType, primitiveField.getContainingType());
|
||||
assertEquals(UnittestProto.getDescriptor(), primitiveField.getFile());
|
||||
assertEquals(FieldDescriptor.Type.INT32, primitiveField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.INT, primitiveField.getJavaType());
|
||||
assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(),
|
||||
primitiveField.getOptions());
|
||||
assertFalse(primitiveField.isExtension());
|
||||
assertEquals("optional_int32", primitiveField.toProto().getName());
|
||||
|
||||
assertEquals("optional_nested_enum", enumField.getName());
|
||||
assertEquals(FieldDescriptor.Type.ENUM, enumField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.ENUM, enumField.getJavaType());
|
||||
assertEquals(TestAllTypes.NestedEnum.getDescriptor(),
|
||||
enumField.getEnumType());
|
||||
|
||||
assertEquals("optional_foreign_message", messageField.getName());
|
||||
assertEquals(FieldDescriptor.Type.MESSAGE, messageField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.MESSAGE, messageField.getJavaType());
|
||||
assertEquals(ForeignMessage.getDescriptor(), messageField.getMessageType());
|
||||
|
||||
assertEquals("optional_cord", cordField.getName());
|
||||
assertEquals(FieldDescriptor.Type.STRING, cordField.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.STRING, cordField.getJavaType());
|
||||
assertEquals(DescriptorProtos.FieldOptions.CType.CORD,
|
||||
cordField.getOptions().getCtype());
|
||||
|
||||
assertEquals("optional_int32_extension", extension.getName());
|
||||
assertEquals("protobuf_unittest.optional_int32_extension",
|
||||
extension.getFullName());
|
||||
assertEquals(1, extension.getNumber());
|
||||
assertEquals(TestAllExtensions.getDescriptor(),
|
||||
extension.getContainingType());
|
||||
assertEquals(UnittestProto.getDescriptor(), extension.getFile());
|
||||
assertEquals(FieldDescriptor.Type.INT32, extension.getType());
|
||||
assertEquals(FieldDescriptor.JavaType.INT, extension.getJavaType());
|
||||
assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(),
|
||||
extension.getOptions());
|
||||
assertTrue(extension.isExtension());
|
||||
assertEquals(null, extension.getExtensionScope());
|
||||
assertEquals("optional_int32_extension", extension.toProto().getName());
|
||||
|
||||
assertEquals("single", nestedExtension.getName());
|
||||
assertEquals("protobuf_unittest.TestRequired.single",
|
||||
nestedExtension.getFullName());
|
||||
assertEquals(TestRequired.getDescriptor(),
|
||||
nestedExtension.getExtensionScope());
|
||||
}
|
||||
|
||||
public void testFieldDescriptorLabel() throws Exception {
|
||||
FieldDescriptor requiredField =
|
||||
TestRequired.getDescriptor().findFieldByName("a");
|
||||
FieldDescriptor optionalField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("optional_int32");
|
||||
FieldDescriptor repeatedField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("repeated_int32");
|
||||
|
||||
assertTrue(requiredField.isRequired());
|
||||
assertFalse(requiredField.isRepeated());
|
||||
assertFalse(optionalField.isRequired());
|
||||
assertFalse(optionalField.isRepeated());
|
||||
assertFalse(repeatedField.isRequired());
|
||||
assertTrue(repeatedField.isRepeated());
|
||||
}
|
||||
|
||||
public void testFieldDescriptorDefault() throws Exception {
|
||||
Descriptor d = TestAllTypes.getDescriptor();
|
||||
assertFalse(d.findFieldByName("optional_int32").hasDefaultValue());
|
||||
assertEquals(0, d.findFieldByName("optional_int32").getDefaultValue());
|
||||
assertTrue(d.findFieldByName("default_int32").hasDefaultValue());
|
||||
assertEquals(41, d.findFieldByName("default_int32").getDefaultValue());
|
||||
|
||||
d = TestExtremeDefaultValues.getDescriptor();
|
||||
assertEquals(
|
||||
ByteString.copyFrom(
|
||||
"\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes("ISO-8859-1")),
|
||||
d.findFieldByName("escaped_bytes").getDefaultValue());
|
||||
assertEquals(-1, d.findFieldByName("large_uint32").getDefaultValue());
|
||||
assertEquals(-1L, d.findFieldByName("large_uint64").getDefaultValue());
|
||||
}
|
||||
|
||||
public void testEnumDescriptor() throws Exception {
|
||||
EnumDescriptor enumType = ForeignEnum.getDescriptor();
|
||||
EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor();
|
||||
|
||||
assertEquals("ForeignEnum", enumType.getName());
|
||||
assertEquals("protobuf_unittest.ForeignEnum", enumType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), enumType.getFile());
|
||||
assertNull(enumType.getContainingType());
|
||||
assertEquals(DescriptorProtos.EnumOptions.getDefaultInstance(),
|
||||
enumType.getOptions());
|
||||
|
||||
assertEquals("NestedEnum", nestedType.getName());
|
||||
assertEquals("protobuf_unittest.TestAllTypes.NestedEnum",
|
||||
nestedType.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), nestedType.getFile());
|
||||
assertEquals(TestAllTypes.getDescriptor(), nestedType.getContainingType());
|
||||
|
||||
EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor();
|
||||
assertEquals(value, enumType.getValues().get(0));
|
||||
assertEquals("FOREIGN_FOO", value.getName());
|
||||
assertEquals(4, value.getNumber());
|
||||
assertEquals(value, enumType.findValueByName("FOREIGN_FOO"));
|
||||
assertEquals(value, enumType.findValueByNumber(4));
|
||||
assertNull(enumType.findValueByName("NO_SUCH_VALUE"));
|
||||
for (int i = 0; i < enumType.getValues().size(); i++) {
|
||||
assertEquals(i, enumType.getValues().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void testServiceDescriptor() throws Exception {
|
||||
ServiceDescriptor service = TestService.getDescriptor();
|
||||
|
||||
assertEquals("TestService", service.getName());
|
||||
assertEquals("protobuf_unittest.TestService", service.getFullName());
|
||||
assertEquals(UnittestProto.getDescriptor(), service.getFile());
|
||||
|
||||
assertEquals(2, service.getMethods().size());
|
||||
|
||||
MethodDescriptor fooMethod = service.getMethods().get(0);
|
||||
assertEquals("Foo", fooMethod.getName());
|
||||
assertEquals(UnittestProto.FooRequest.getDescriptor(),
|
||||
fooMethod.getInputType());
|
||||
assertEquals(UnittestProto.FooResponse.getDescriptor(),
|
||||
fooMethod.getOutputType());
|
||||
assertEquals(fooMethod, service.findMethodByName("Foo"));
|
||||
|
||||
MethodDescriptor barMethod = service.getMethods().get(1);
|
||||
assertEquals("Bar", barMethod.getName());
|
||||
assertEquals(UnittestProto.BarRequest.getDescriptor(),
|
||||
barMethod.getInputType());
|
||||
assertEquals(UnittestProto.BarResponse.getDescriptor(),
|
||||
barMethod.getOutputType());
|
||||
assertEquals(barMethod, service.findMethodByName("Bar"));
|
||||
|
||||
assertNull(service.findMethodByName("NoSuchMethod"));
|
||||
|
||||
for (int i = 0; i < service.getMethods().size(); i++) {
|
||||
assertEquals(i, service.getMethods().get(i).getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testCustomOptions() throws Exception {
|
||||
Descriptor descriptor =
|
||||
UnittestCustomOptions.TestMessageWithCustomOptions.getDescriptor();
|
||||
|
||||
assertTrue(
|
||||
descriptor.getOptions().hasExtension(UnittestCustomOptions.messageOpt1));
|
||||
assertEquals(Integer.valueOf(-56),
|
||||
descriptor.getOptions().getExtension(UnittestCustomOptions.messageOpt1));
|
||||
|
||||
FieldDescriptor field = descriptor.findFieldByName("field1");
|
||||
assertNotNull(field);
|
||||
|
||||
assertTrue(
|
||||
field.getOptions().hasExtension(UnittestCustomOptions.fieldOpt1));
|
||||
assertEquals(Long.valueOf(8765432109L),
|
||||
field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1));
|
||||
|
||||
EnumDescriptor enumType =
|
||||
UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor();
|
||||
|
||||
assertTrue(
|
||||
enumType.getOptions().hasExtension(UnittestCustomOptions.enumOpt1));
|
||||
assertEquals(Integer.valueOf(-789),
|
||||
enumType.getOptions().getExtension(UnittestCustomOptions.enumOpt1));
|
||||
|
||||
ServiceDescriptor service =
|
||||
UnittestCustomOptions.TestServiceWithCustomOptions.getDescriptor();
|
||||
|
||||
assertTrue(
|
||||
service.getOptions().hasExtension(UnittestCustomOptions.serviceOpt1));
|
||||
assertEquals(Long.valueOf(-9876543210L),
|
||||
service.getOptions().getExtension(UnittestCustomOptions.serviceOpt1));
|
||||
|
||||
MethodDescriptor method = service.findMethodByName("Foo");
|
||||
assertNotNull(method);
|
||||
|
||||
assertTrue(
|
||||
method.getOptions().hasExtension(UnittestCustomOptions.methodOpt1));
|
||||
assertEquals(UnittestCustomOptions.MethodOpt1.METHODOPT1_VAL2,
|
||||
method.getOptions().getExtension(UnittestCustomOptions.methodOpt1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the FieldDescriptor.Type enum is the same as the
|
||||
* WireFormat.FieldType enum.
|
||||
*/
|
||||
public void testFieldTypeTablesMatch() throws Exception {
|
||||
FieldDescriptor.Type[] values1 = FieldDescriptor.Type.values();
|
||||
WireFormat.FieldType[] values2 = WireFormat.FieldType.values();
|
||||
|
||||
assertEquals(values1.length, values2.length);
|
||||
|
||||
for (int i = 0; i < values1.length; i++) {
|
||||
assertEquals(values1[i].toString(), values2[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the FieldDescriptor.JavaType enum is the same as the
|
||||
* WireFormat.JavaType enum.
|
||||
*/
|
||||
public void testJavaTypeTablesMatch() throws Exception {
|
||||
FieldDescriptor.JavaType[] values1 = FieldDescriptor.JavaType.values();
|
||||
WireFormat.JavaType[] values2 = WireFormat.JavaType.values();
|
||||
|
||||
assertEquals(values1.length, values2.length);
|
||||
|
||||
for (int i = 0; i < values1.length; i++) {
|
||||
assertEquals(values1[i].toString(), values2[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void testEnormousDescriptor() throws Exception {
|
||||
// The descriptor for this file is larger than 64k, yet it did not cause
|
||||
// a compiler error due to an over-long string literal.
|
||||
assertTrue(
|
||||
UnittestEnormousDescriptor.getDescriptor()
|
||||
.toProto().getSerializedSize() > 65536);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the DescriptorValidationException works as intended.
|
||||
*/
|
||||
public void testDescriptorValidatorException() throws Exception {
|
||||
FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setType(FieldDescriptorProto.Type.TYPE_INT32)
|
||||
.setName("foo")
|
||||
.setNumber(1)
|
||||
.setDefaultValue("invalid")
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(fileDescriptorProto,
|
||||
new FileDescriptor[0]);
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
// Expected; check that the error message contains some useful hints
|
||||
assertTrue(e.getMessage().indexOf("foo") != -1);
|
||||
assertTrue(e.getMessage().indexOf("Foo") != -1);
|
||||
assertTrue(e.getMessage().indexOf("invalid") != -1);
|
||||
assertTrue(e.getCause() instanceof NumberFormatException);
|
||||
assertTrue(e.getCause().getMessage().indexOf("invalid") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the translate/crosslink for an example where a message field's name
|
||||
* and type name are the same.
|
||||
*/
|
||||
public void testDescriptorComplexCrosslink() throws Exception {
|
||||
FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setType(FieldDescriptorProto.Type.TYPE_INT32)
|
||||
.setName("foo")
|
||||
.setNumber(1)
|
||||
.build())
|
||||
.build())
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Bar")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Foo")
|
||||
.setName("Foo")
|
||||
.setNumber(1)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
// translate and crosslink
|
||||
FileDescriptor file =
|
||||
Descriptors.FileDescriptor.buildFrom(fileDescriptorProto,
|
||||
new FileDescriptor[0]);
|
||||
// verify resulting descriptors
|
||||
assertNotNull(file);
|
||||
List<Descriptor> msglist = file.getMessageTypes();
|
||||
assertNotNull(msglist);
|
||||
assertTrue(msglist.size() == 2);
|
||||
boolean barFound = false;
|
||||
for (Descriptor desc : msglist) {
|
||||
if (desc.getName().equals("Bar")) {
|
||||
barFound = true;
|
||||
assertNotNull(desc.getFields());
|
||||
List<FieldDescriptor> fieldlist = desc.getFields();
|
||||
assertNotNull(fieldlist);
|
||||
assertTrue(fieldlist.size() == 1);
|
||||
assertTrue(fieldlist.get(0).getType() == FieldDescriptor.Type.MESSAGE);
|
||||
assertTrue(fieldlist.get(0).getMessageType().getName().equals("Foo"));
|
||||
}
|
||||
}
|
||||
assertTrue(barFound);
|
||||
}
|
||||
|
||||
public void testInvalidPublicDependency() throws Exception {
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto") .build();
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("boo.proto")
|
||||
.addDependency("foo.proto")
|
||||
.addPublicDependency(1) // Error, should be 0.
|
||||
.build();
|
||||
FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(fooProto,
|
||||
new FileDescriptor[0]);
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(barProto,
|
||||
new FileDescriptor[] {fooFile});
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
assertTrue(
|
||||
e.getMessage().indexOf("Invalid public dependency index.") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testHiddenDependency() throws Exception {
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder().setName("Bar"))
|
||||
.build();
|
||||
FileDescriptorProto forwardProto = FileDescriptorProto.newBuilder()
|
||||
.setName("forward.proto")
|
||||
.addDependency("bar.proto")
|
||||
.build();
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("forward.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Bar")
|
||||
.setName("bar")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(
|
||||
barProto, new FileDescriptor[0]);
|
||||
FileDescriptor forwardFile = Descriptors.FileDescriptor.buildFrom(
|
||||
forwardProto, new FileDescriptor[] {barFile});
|
||||
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(
|
||||
fooProto, new FileDescriptor[] {forwardFile});
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
assertTrue(e.getMessage().indexOf("Bar") != -1);
|
||||
assertTrue(e.getMessage().indexOf("is not defined") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testPublicDependency() throws Exception {
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder().setName("Bar"))
|
||||
.build();
|
||||
FileDescriptorProto forwardProto = FileDescriptorProto.newBuilder()
|
||||
.setName("forward.proto")
|
||||
.addDependency("bar.proto")
|
||||
.addPublicDependency(0)
|
||||
.build();
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("forward.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Bar")
|
||||
.setName("bar")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(
|
||||
barProto, new FileDescriptor[0]);
|
||||
FileDescriptor forwardFile = Descriptors.FileDescriptor.buildFrom(
|
||||
forwardProto, new FileDescriptor[]{barFile});
|
||||
Descriptors.FileDescriptor.buildFrom(
|
||||
fooProto, new FileDescriptor[] {forwardFile});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the translate/crosslink for an example with a more complex namespace
|
||||
* referencing.
|
||||
*/
|
||||
public void testComplexNamespacePublicDependency() throws Exception {
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
.setPackage("a.b.c.d.bar.shared")
|
||||
.addEnumType(EnumDescriptorProto.newBuilder()
|
||||
.setName("MyEnum")
|
||||
.addValue(EnumValueDescriptorProto.newBuilder()
|
||||
.setName("BLAH")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("bar.proto")
|
||||
.setPackage("a.b.c.d.foo.shared")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("MyMessage")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_REPEATED)
|
||||
.setTypeName("bar.shared.MyEnum")
|
||||
.setName("MyField")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
// translate and crosslink
|
||||
FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(
|
||||
fooProto, new FileDescriptor[0]);
|
||||
FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(
|
||||
barProto, new FileDescriptor[]{fooFile});
|
||||
// verify resulting descriptors
|
||||
assertNotNull(barFile);
|
||||
List<Descriptor> msglist = barFile.getMessageTypes();
|
||||
assertNotNull(msglist);
|
||||
assertTrue(msglist.size() == 1);
|
||||
Descriptor desc = msglist.get(0);
|
||||
if (desc.getName().equals("MyMessage")) {
|
||||
assertNotNull(desc.getFields());
|
||||
List<FieldDescriptor> fieldlist = desc.getFields();
|
||||
assertNotNull(fieldlist);
|
||||
assertTrue(fieldlist.size() == 1);
|
||||
FieldDescriptor field = fieldlist.get(0);
|
||||
assertTrue(field.getType() == FieldDescriptor.Type.ENUM);
|
||||
assertTrue(field.getEnumType().getName().equals("MyEnum"));
|
||||
assertTrue(field.getEnumType().getFile().getName().equals("bar.proto"));
|
||||
assertTrue(field.getEnumType().getFile().getPackage().equals(
|
||||
"a.b.c.d.bar.shared"));
|
||||
}
|
||||
}
|
||||
}
|
||||
264
java/src/test/java/com/google/protobuf/DynamicMessageTest.java
Normal file
264
java/src/test/java/com/google/protobuf/DynamicMessageTest.java
Normal file
@@ -0,0 +1,264 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Unit test for {@link DynamicMessage}. See also {@link MessageTest}, which
|
||||
* tests some {@link DynamicMessage} functionality.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class DynamicMessageTest extends TestCase {
|
||||
TestUtil.ReflectionTester reflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null);
|
||||
|
||||
TestUtil.ReflectionTester extensionsReflectionTester =
|
||||
new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
TestUtil.ReflectionTester packedReflectionTester =
|
||||
new TestUtil.ReflectionTester(TestPackedTypes.getDescriptor(), null);
|
||||
|
||||
public void testDynamicMessageAccessors() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
}
|
||||
|
||||
public void testSettersAfterBuild() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
Message firstMessage = builder.build();
|
||||
// double build()
|
||||
builder.build();
|
||||
// clear() after build()
|
||||
builder.clear();
|
||||
// setters after build()
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
// repeated setters after build()
|
||||
reflectionTester.modifyRepeatedFieldsViaReflection(builder);
|
||||
message = builder.build();
|
||||
reflectionTester.assertRepeatedFieldsModifiedViaReflection(message);
|
||||
// firstMessage shouldn't have been modified.
|
||||
reflectionTester.assertClearViaReflection(firstMessage);
|
||||
}
|
||||
|
||||
public void testUnknownFields() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestEmptyMessage.getDescriptor());
|
||||
builder.setUnknownFields(UnknownFieldSet.newBuilder()
|
||||
.addField(1, UnknownFieldSet.Field.newBuilder().addVarint(1).build())
|
||||
.addField(2, UnknownFieldSet.Field.newBuilder().addFixed32(1).build())
|
||||
.build());
|
||||
Message message = builder.build();
|
||||
assertEquals(2, message.getUnknownFields().asMap().size());
|
||||
// clone() with unknown fields
|
||||
Message.Builder newBuilder = builder.clone();
|
||||
assertEquals(2, newBuilder.getUnknownFields().asMap().size());
|
||||
// clear() with unknown fields
|
||||
newBuilder.clear();
|
||||
assertTrue(newBuilder.getUnknownFields().asMap().isEmpty());
|
||||
// serialize/parse with unknown fields
|
||||
newBuilder.mergeFrom(message.toByteString());
|
||||
assertEquals(2, newBuilder.getUnknownFields().asMap().size());
|
||||
}
|
||||
|
||||
public void testDynamicMessageSettersRejectNull() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.assertReflectionSettersRejectNull(builder);
|
||||
}
|
||||
|
||||
public void testDynamicMessageExtensionAccessors() throws Exception {
|
||||
// We don't need to extensively test DynamicMessage's handling of
|
||||
// extensions because, frankly, it doesn't do anything special with them.
|
||||
// It treats them just like any other fields.
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllExtensions.getDescriptor());
|
||||
extensionsReflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
}
|
||||
|
||||
public void testDynamicMessageExtensionSettersRejectNull() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllExtensions.getDescriptor());
|
||||
extensionsReflectionTester.assertReflectionSettersRejectNull(builder);
|
||||
}
|
||||
|
||||
public void testDynamicMessageRepeatedSetters() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
reflectionTester.modifyRepeatedFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
reflectionTester.assertRepeatedFieldsModifiedViaReflection(message);
|
||||
}
|
||||
|
||||
public void testDynamicMessageRepeatedSettersRejectNull() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.assertReflectionRepeatedSettersRejectNull(builder);
|
||||
}
|
||||
|
||||
public void testDynamicMessageDefaults() throws Exception {
|
||||
reflectionTester.assertClearViaReflection(
|
||||
DynamicMessage.getDefaultInstance(TestAllTypes.getDescriptor()));
|
||||
reflectionTester.assertClearViaReflection(
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor()).build());
|
||||
}
|
||||
|
||||
public void testDynamicMessageSerializedSize() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
Message.Builder dynamicBuilder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(dynamicBuilder);
|
||||
Message dynamicMessage = dynamicBuilder.build();
|
||||
|
||||
assertEquals(message.getSerializedSize(),
|
||||
dynamicMessage.getSerializedSize());
|
||||
}
|
||||
|
||||
public void testDynamicMessageSerialization() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
|
||||
// In fact, the serialized forms should be exactly the same, byte-for-byte.
|
||||
assertEquals(TestUtil.getAllSet().toByteString(), rawBytes);
|
||||
}
|
||||
|
||||
public void testDynamicMessageParsing() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
Message message2 =
|
||||
DynamicMessage.parseFrom(TestAllTypes.getDescriptor(), rawBytes);
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message2);
|
||||
|
||||
// Test Parser interface.
|
||||
Message message3 = message2.getParserForType().parseFrom(rawBytes);
|
||||
reflectionTester.assertAllFieldsSetViaReflection(message3);
|
||||
}
|
||||
|
||||
public void testDynamicMessageExtensionParsing() throws Exception {
|
||||
ByteString rawBytes = TestUtil.getAllExtensionsSet().toByteString();
|
||||
Message message = DynamicMessage.parseFrom(
|
||||
TestAllExtensions.getDescriptor(), rawBytes,
|
||||
TestUtil.getExtensionRegistry());
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message);
|
||||
|
||||
// Test Parser interface.
|
||||
Message message2 = message.getParserForType().parseFrom(
|
||||
rawBytes, TestUtil.getExtensionRegistry());
|
||||
extensionsReflectionTester.assertAllFieldsSetViaReflection(message2);
|
||||
}
|
||||
|
||||
public void testDynamicMessagePackedSerialization() throws Exception {
|
||||
Message.Builder builder =
|
||||
DynamicMessage.newBuilder(TestPackedTypes.getDescriptor());
|
||||
packedReflectionTester.setPackedFieldsViaReflection(builder);
|
||||
Message message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
TestPackedTypes message2 = TestPackedTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertPackedFieldsSet(message2);
|
||||
|
||||
// In fact, the serialized forms should be exactly the same, byte-for-byte.
|
||||
assertEquals(TestUtil.getPackedSet().toByteString(), rawBytes);
|
||||
}
|
||||
|
||||
public void testDynamicMessagePackedParsing() throws Exception {
|
||||
TestPackedTypes.Builder builder = TestPackedTypes.newBuilder();
|
||||
TestUtil.setPackedFields(builder);
|
||||
TestPackedTypes message = builder.build();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
Message message2 =
|
||||
DynamicMessage.parseFrom(TestPackedTypes.getDescriptor(), rawBytes);
|
||||
packedReflectionTester.assertPackedFieldsSetViaReflection(message2);
|
||||
|
||||
// Test Parser interface.
|
||||
Message message3 = message2.getParserForType().parseFrom(rawBytes);
|
||||
packedReflectionTester.assertPackedFieldsSetViaReflection(message3);
|
||||
}
|
||||
|
||||
public void testDynamicMessageCopy() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
DynamicMessage copy = DynamicMessage.newBuilder(message).build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(copy);
|
||||
}
|
||||
|
||||
public void testToBuilder() throws Exception {
|
||||
DynamicMessage.Builder builder =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor());
|
||||
reflectionTester.setAllFieldsViaReflection(builder);
|
||||
int unknownFieldNum = 9;
|
||||
long unknownFieldVal = 90;
|
||||
builder.setUnknownFields(UnknownFieldSet.newBuilder()
|
||||
.addField(unknownFieldNum,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(unknownFieldVal).build())
|
||||
.build());
|
||||
DynamicMessage message = builder.build();
|
||||
|
||||
DynamicMessage derived = message.toBuilder().build();
|
||||
reflectionTester.assertAllFieldsSetViaReflection(derived);
|
||||
assertEquals(Arrays.asList(unknownFieldVal),
|
||||
derived.getUnknownFields().getField(unknownFieldNum).getVarintList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* A prerun for a test suite that allows running the full protocol buffer
|
||||
* tests in a mode that disables the optimization for not using
|
||||
* {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder} until they are
|
||||
* requested. This allows us to run all the tests through both code paths
|
||||
* and ensures that both code paths produce identical results.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class ForceFieldBuildersPreRun implements Runnable {
|
||||
|
||||
//@Override (Java 1.6 override semantics, but we must support 1.5)
|
||||
public void run() {
|
||||
GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
|
||||
}
|
||||
}
|
||||
1146
java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
Normal file
1146
java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
Normal file
File diff suppressed because it is too large
Load Diff
180
java/src/test/java/com/google/protobuf/IsValidUtf8Test.java
Normal file
180
java/src/test/java/com/google/protobuf/IsValidUtf8Test.java
Normal file
@@ -0,0 +1,180 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.IsValidUtf8TestUtil.Shard;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Tests cases for {@link ByteString#isValidUtf8()}. This includes three
|
||||
* brute force tests that actually test every permutation of one byte, two byte,
|
||||
* and three byte sequences to ensure that the method produces the right result
|
||||
* for every possible byte encoding where "right" means it's consistent with
|
||||
* java's UTF-8 string encoding/decoding such that the method returns true for
|
||||
* any sequence that will round trip when converted to a String and then back to
|
||||
* bytes and will return false for any sequence that will not round trip.
|
||||
* See also {@link IsValidUtf8FourByteTest}. It also includes some
|
||||
* other more targeted tests.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
public class IsValidUtf8Test extends TestCase {
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all two byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_1Byte() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(1,
|
||||
IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all two byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_2Bytes() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(2,
|
||||
IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all three byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_3Bytes() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(3,
|
||||
IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of a sample of four byte permutations work.
|
||||
* All permutations are prohibitively expensive to test for automated runs;
|
||||
* {@link IsValidUtf8FourByteTest} is used for full coverage. This method
|
||||
* tests specific four-byte cases.
|
||||
*/
|
||||
public void testIsValidUtf8_4BytesSamples()
|
||||
throws UnsupportedEncodingException {
|
||||
// Valid 4 byte.
|
||||
assertValidUtf8(0xF0, 0xA4, 0xAD, 0xA2);
|
||||
|
||||
// Bad trailing bytes
|
||||
assertInvalidUtf8(0xF0, 0xA4, 0xAD, 0x7F);
|
||||
assertInvalidUtf8(0xF0, 0xA4, 0xAD, 0xC0);
|
||||
|
||||
// Special cases for byte2
|
||||
assertInvalidUtf8(0xF0, 0x8F, 0xAD, 0xA2);
|
||||
assertInvalidUtf8(0xF4, 0x90, 0xAD, 0xA2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests some hard-coded test cases.
|
||||
*/
|
||||
public void testSomeSequences() {
|
||||
// Empty
|
||||
assertTrue(asBytes("").isValidUtf8());
|
||||
|
||||
// One-byte characters, including control characters
|
||||
assertTrue(asBytes("\u0000abc\u007f").isValidUtf8());
|
||||
|
||||
// Two-byte characters
|
||||
assertTrue(asBytes("\u00a2\u00a2").isValidUtf8());
|
||||
|
||||
// Three-byte characters
|
||||
assertTrue(asBytes("\u020ac\u020ac").isValidUtf8());
|
||||
|
||||
// Four-byte characters
|
||||
assertTrue(asBytes("\u024B62\u024B62").isValidUtf8());
|
||||
|
||||
// Mixed string
|
||||
assertTrue(
|
||||
asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62")
|
||||
.isValidUtf8());
|
||||
|
||||
// Not a valid string
|
||||
assertInvalidUtf8(-1, 0, -1, 0);
|
||||
}
|
||||
|
||||
private byte[] toByteArray(int... bytes) {
|
||||
byte[] realBytes = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
realBytes[i] = (byte) bytes[i];
|
||||
}
|
||||
return realBytes;
|
||||
}
|
||||
|
||||
private ByteString toByteString(int... bytes) {
|
||||
return ByteString.copyFrom(toByteArray(bytes));
|
||||
}
|
||||
|
||||
private void assertValidUtf8(int[] bytes, boolean not) {
|
||||
byte[] realBytes = toByteArray(bytes);
|
||||
assertTrue(not ^ Utf8.isValidUtf8(realBytes));
|
||||
assertTrue(not ^ Utf8.isValidUtf8(realBytes, 0, bytes.length));
|
||||
ByteString lit = ByteString.copyFrom(realBytes);
|
||||
ByteString sub = lit.substring(0, bytes.length);
|
||||
assertTrue(not ^ lit.isValidUtf8());
|
||||
assertTrue(not ^ sub.isValidUtf8());
|
||||
ByteString[] ropes = {
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, lit),
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
|
||||
RopeByteString.newInstanceForTest(lit, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, lit)
|
||||
};
|
||||
for (ByteString rope : ropes) {
|
||||
assertTrue(not ^ rope.isValidUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidUtf8(int... bytes) {
|
||||
assertValidUtf8(bytes, false);
|
||||
}
|
||||
|
||||
private void assertInvalidUtf8(int... bytes) {
|
||||
assertValidUtf8(bytes, true);
|
||||
}
|
||||
|
||||
private static ByteString asBytes(String s) {
|
||||
return ByteString.copyFromUtf8(s);
|
||||
}
|
||||
|
||||
public void testShardsHaveExpectedRoundTrippables() {
|
||||
// A sanity check.
|
||||
int actual = 0;
|
||||
for (Shard shard : IsValidUtf8TestUtil.FOUR_BYTE_SHARDS) {
|
||||
actual += shard.expected;
|
||||
}
|
||||
assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT,
|
||||
actual);
|
||||
}
|
||||
}
|
||||
421
java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
Normal file
421
java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
Normal file
@@ -0,0 +1,421 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Logger;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
/**
|
||||
* Shared testing code for {@link IsValidUtf8Test} and
|
||||
* {@link IsValidUtf8FourByteTest}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
class IsValidUtf8TestUtil {
|
||||
private static Logger logger = Logger.getLogger(
|
||||
IsValidUtf8TestUtil.class.getName());
|
||||
|
||||
// 128 - [chars 0x0000 to 0x007f]
|
||||
static long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1;
|
||||
|
||||
// 128
|
||||
static long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 1920 [chars 0x0080 to 0x07FF]
|
||||
static long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1;
|
||||
|
||||
// 18,304
|
||||
static long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// Both bytes are one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 2) +
|
||||
// The possible number of two byte characters
|
||||
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 2048
|
||||
static long THREE_BYTE_SURROGATES = 2 * 1024;
|
||||
|
||||
// 61,440 [chars 0x0800 to 0xFFFF, minus surrogates]
|
||||
static long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS =
|
||||
0xFFFF - 0x0800 + 1 - THREE_BYTE_SURROGATES;
|
||||
|
||||
// 2,650,112
|
||||
static long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// All one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 3) +
|
||||
// One two byte character and a one byte character
|
||||
2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Three byte characters
|
||||
THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 1,048,576 [chars 0x10000L to 0x10FFFF]
|
||||
static long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1;
|
||||
|
||||
// 289,571,839
|
||||
static long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// All one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 4) +
|
||||
// One and three byte characters
|
||||
2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Two two byte characters
|
||||
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Permutations of one and two byte characters
|
||||
3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Four byte characters
|
||||
FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
static class Shard {
|
||||
final long index;
|
||||
final long start;
|
||||
final long lim;
|
||||
final long expected;
|
||||
|
||||
|
||||
public Shard(long index, long start, long lim, long expected) {
|
||||
assertTrue(start < lim);
|
||||
this.index = index;
|
||||
this.start = start;
|
||||
this.lim = lim;
|
||||
this.expected = expected;
|
||||
}
|
||||
}
|
||||
|
||||
static final long[] FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES =
|
||||
generateFourByteShardsExpectedRunnables();
|
||||
|
||||
private static long[] generateFourByteShardsExpectedRunnables() {
|
||||
long[] expected = new long[128];
|
||||
|
||||
// 0-63 are all 5300224
|
||||
for (int i = 0; i <= 63; i++) {
|
||||
expected[i] = 5300224;
|
||||
}
|
||||
|
||||
// 97-111 are all 2342912
|
||||
for (int i = 97; i <= 111; i++) {
|
||||
expected[i] = 2342912;
|
||||
}
|
||||
|
||||
// 113-117 are all 1048576
|
||||
for (int i = 113; i <= 117; i++) {
|
||||
expected[i] = 1048576;
|
||||
}
|
||||
|
||||
// One offs
|
||||
expected[112] = 786432;
|
||||
expected[118] = 786432;
|
||||
expected[119] = 1048576;
|
||||
expected[120] = 458752;
|
||||
expected[121] = 524288;
|
||||
expected[122] = 65536;
|
||||
|
||||
// Anything not assigned was the default 0.
|
||||
return expected;
|
||||
}
|
||||
|
||||
static final List<Shard> FOUR_BYTE_SHARDS = generateFourByteShards(
|
||||
128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES);
|
||||
|
||||
|
||||
private static List<Shard> generateFourByteShards(
|
||||
int numShards, long[] expected) {
|
||||
assertEquals(numShards, expected.length);
|
||||
List<Shard> shards = new ArrayList<Shard>(numShards);
|
||||
long LIM = 1L << 32;
|
||||
long increment = LIM / numShards;
|
||||
assertTrue(LIM % numShards == 0);
|
||||
for (int i = 0; i < numShards; i++) {
|
||||
shards.add(new Shard(i,
|
||||
increment * i,
|
||||
increment * (i + 1),
|
||||
expected[i]));
|
||||
}
|
||||
return shards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to run the loop to test all the permutations for the number of bytes
|
||||
* specified.
|
||||
*
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
*/
|
||||
static void testBytes(int numBytes, long expectedCount)
|
||||
throws UnsupportedEncodingException {
|
||||
testBytes(numBytes, expectedCount, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to run the loop to test all the permutations for the number of bytes
|
||||
* specified. This overload is useful for debugging to get the loop to start
|
||||
* at a certain character.
|
||||
*
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
* @param start the starting bytes encoded as a long as big-endian
|
||||
* @param lim the limit of bytes to process encoded as a long as big-endian,
|
||||
* or -1 to mean the max limit for numBytes
|
||||
*/
|
||||
static void testBytes(int numBytes, long expectedCount, long start, long lim)
|
||||
throws UnsupportedEncodingException {
|
||||
Random rnd = new Random();
|
||||
byte[] bytes = new byte[numBytes];
|
||||
|
||||
if (lim == -1) {
|
||||
lim = 1L << (numBytes * 8);
|
||||
}
|
||||
long count = 0;
|
||||
long countRoundTripped = 0;
|
||||
for (long byteChar = start; byteChar < lim; byteChar++) {
|
||||
long tmpByteChar = byteChar;
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
|
||||
tmpByteChar = tmpByteChar >> 8;
|
||||
}
|
||||
ByteString bs = ByteString.copyFrom(bytes);
|
||||
boolean isRoundTrippable = bs.isValidUtf8();
|
||||
String s = new String(bytes, "UTF-8");
|
||||
byte[] bytesReencoded = s.getBytes("UTF-8");
|
||||
boolean bytesEqual = Arrays.equals(bytes, bytesReencoded);
|
||||
|
||||
if (bytesEqual != isRoundTrippable) {
|
||||
outputFailure(byteChar, bytes, bytesReencoded);
|
||||
}
|
||||
|
||||
// Check agreement with static Utf8 methods.
|
||||
assertEquals(isRoundTrippable, Utf8.isValidUtf8(bytes));
|
||||
assertEquals(isRoundTrippable, Utf8.isValidUtf8(bytes, 0, numBytes));
|
||||
|
||||
// Test partial sequences.
|
||||
// Partition numBytes into three segments (not necessarily non-empty).
|
||||
int i = rnd.nextInt(numBytes);
|
||||
int j = rnd.nextInt(numBytes);
|
||||
if (j < i) {
|
||||
int tmp = i; i = j; j = tmp;
|
||||
}
|
||||
int state1 = Utf8.partialIsValidUtf8(Utf8.COMPLETE, bytes, 0, i);
|
||||
int state2 = Utf8.partialIsValidUtf8(state1, bytes, i, j);
|
||||
int state3 = Utf8.partialIsValidUtf8(state2, bytes, j, numBytes);
|
||||
if (isRoundTrippable != (state3 == Utf8.COMPLETE)) {
|
||||
System.out.printf("state=%04x %04x %04x i=%d j=%d%n",
|
||||
state1, state2, state3, i, j);
|
||||
outputFailure(byteChar, bytes, bytesReencoded);
|
||||
}
|
||||
assertEquals(isRoundTrippable, (state3 == Utf8.COMPLETE));
|
||||
|
||||
// Test ropes built out of small partial sequences
|
||||
ByteString rope = RopeByteString.newInstanceForTest(
|
||||
bs.substring(0, i),
|
||||
RopeByteString.newInstanceForTest(
|
||||
bs.substring(i, j),
|
||||
bs.substring(j, numBytes)));
|
||||
assertSame(RopeByteString.class, rope.getClass());
|
||||
|
||||
ByteString[] byteStrings = { bs, bs.substring(0, numBytes), rope };
|
||||
for (ByteString x : byteStrings) {
|
||||
assertEquals(isRoundTrippable,
|
||||
x.isValidUtf8());
|
||||
assertEquals(state3,
|
||||
x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes));
|
||||
|
||||
assertEquals(state1,
|
||||
x.partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state1,
|
||||
x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state2,
|
||||
x.partialIsValidUtf8(state1, i, j - i));
|
||||
assertEquals(state2,
|
||||
x.substring(i, j).partialIsValidUtf8(state1, 0, j - i));
|
||||
assertEquals(state3,
|
||||
x.partialIsValidUtf8(state2, j, numBytes - j));
|
||||
assertEquals(state3,
|
||||
x.substring(j, numBytes)
|
||||
.partialIsValidUtf8(state2, 0, numBytes - j));
|
||||
}
|
||||
|
||||
// ByteString reduplication should not affect its UTF-8 validity.
|
||||
ByteString ropeADope =
|
||||
RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes));
|
||||
assertEquals(isRoundTrippable, ropeADope.isValidUtf8());
|
||||
|
||||
if (isRoundTrippable) {
|
||||
countRoundTripped++;
|
||||
}
|
||||
count++;
|
||||
if (byteChar != 0 && byteChar % 1000000L == 0) {
|
||||
logger.info("Processed " + (byteChar / 1000000L) +
|
||||
" million characters");
|
||||
}
|
||||
}
|
||||
logger.info("Round tripped " + countRoundTripped + " of " + count);
|
||||
assertEquals(expectedCount, countRoundTripped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variation of {@link #testBytes} that does less allocation using the
|
||||
* low-level encoders/decoders directly. Checked in because it's useful for
|
||||
* debugging when trying to process bytes faster, but since it doesn't use the
|
||||
* actual String class, it's possible for incompatibilities to develop
|
||||
* (although unlikely).
|
||||
*
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
* @param start the starting bytes encoded as a long as big-endian
|
||||
* @param lim the limit of bytes to process encoded as a long as big-endian,
|
||||
* or -1 to mean the max limit for numBytes
|
||||
*/
|
||||
void testBytesUsingByteBuffers(
|
||||
int numBytes, long expectedCount, long start, long lim)
|
||||
throws UnsupportedEncodingException {
|
||||
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
byte[] bytes = new byte[numBytes];
|
||||
int maxChars = (int) (decoder.maxCharsPerByte() * numBytes) + 1;
|
||||
char[] charsDecoded =
|
||||
new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1];
|
||||
int maxBytes = (int) (encoder.maxBytesPerChar() * maxChars) + 1;
|
||||
byte[] bytesReencoded = new byte[maxBytes];
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||
CharBuffer cb = CharBuffer.wrap(charsDecoded);
|
||||
ByteBuffer bbReencoded = ByteBuffer.wrap(bytesReencoded);
|
||||
if (lim == -1) {
|
||||
lim = 1L << (numBytes * 8);
|
||||
}
|
||||
long count = 0;
|
||||
long countRoundTripped = 0;
|
||||
for (long byteChar = start; byteChar < lim; byteChar++) {
|
||||
bb.rewind();
|
||||
bb.limit(bytes.length);
|
||||
cb.rewind();
|
||||
cb.limit(charsDecoded.length);
|
||||
bbReencoded.rewind();
|
||||
bbReencoded.limit(bytesReencoded.length);
|
||||
encoder.reset();
|
||||
decoder.reset();
|
||||
long tmpByteChar = byteChar;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
|
||||
tmpByteChar = tmpByteChar >> 8;
|
||||
}
|
||||
boolean isRoundTrippable = ByteString.copyFrom(bytes).isValidUtf8();
|
||||
CoderResult result = decoder.decode(bb, cb, true);
|
||||
assertFalse(result.isError());
|
||||
result = decoder.flush(cb);
|
||||
assertFalse(result.isError());
|
||||
|
||||
int charLen = cb.position();
|
||||
cb.rewind();
|
||||
cb.limit(charLen);
|
||||
result = encoder.encode(cb, bbReencoded, true);
|
||||
assertFalse(result.isError());
|
||||
result = encoder.flush(bbReencoded);
|
||||
assertFalse(result.isError());
|
||||
|
||||
boolean bytesEqual = true;
|
||||
int bytesLen = bbReencoded.position();
|
||||
if (bytesLen != numBytes) {
|
||||
bytesEqual = false;
|
||||
} else {
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
if (bytes[i] != bytesReencoded[i]) {
|
||||
bytesEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bytesEqual != isRoundTrippable) {
|
||||
outputFailure(byteChar, bytes, bytesReencoded, bytesLen);
|
||||
}
|
||||
|
||||
count++;
|
||||
if (isRoundTrippable) {
|
||||
countRoundTripped++;
|
||||
}
|
||||
if (byteChar != 0 && byteChar % 1000000 == 0) {
|
||||
logger.info("Processed " + (byteChar / 1000000) +
|
||||
" million characters");
|
||||
}
|
||||
}
|
||||
logger.info("Round tripped " + countRoundTripped + " of " + count);
|
||||
assertEquals(expectedCount, countRoundTripped);
|
||||
}
|
||||
|
||||
private static void outputFailure(long byteChar, byte[] bytes, byte[] after) {
|
||||
outputFailure(byteChar, bytes, after, after.length);
|
||||
}
|
||||
|
||||
private static void outputFailure(long byteChar, byte[] bytes, byte[] after,
|
||||
int len) {
|
||||
fail("Failure: (" + Long.toHexString(byteChar) + ") " +
|
||||
toHexString(bytes) + " => " + toHexString(after, len));
|
||||
}
|
||||
|
||||
private static String toHexString(byte[] b) {
|
||||
return toHexString(b, b.length);
|
||||
}
|
||||
|
||||
private static String toHexString(byte[] b, int len) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("\"");
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
s.append(" ");
|
||||
}
|
||||
s.append(String.format("%02x", b[i] & 0xFF));
|
||||
}
|
||||
s.append("\"");
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for {@link LazyStringArrayList}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringArrayListTest extends TestCase {
|
||||
|
||||
private static String STRING_A = "A";
|
||||
private static String STRING_B = "B";
|
||||
private static String STRING_C = "C";
|
||||
|
||||
private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A");
|
||||
private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B");
|
||||
private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C");
|
||||
|
||||
public void testJustStrings() {
|
||||
LazyStringArrayList list = new LazyStringArrayList();
|
||||
list.add(STRING_A);
|
||||
list.add(STRING_B);
|
||||
list.add(STRING_C);
|
||||
|
||||
assertEquals(3, list.size());
|
||||
assertSame(STRING_A, list.get(0));
|
||||
assertSame(STRING_B, list.get(1));
|
||||
assertSame(STRING_C, list.get(2));
|
||||
|
||||
list.set(1, STRING_C);
|
||||
assertSame(STRING_C, list.get(1));
|
||||
|
||||
list.remove(1);
|
||||
assertSame(STRING_A, list.get(0));
|
||||
assertSame(STRING_C, list.get(1));
|
||||
}
|
||||
|
||||
public void testJustByteString() {
|
||||
LazyStringArrayList list = new LazyStringArrayList();
|
||||
list.add(BYTE_STRING_A);
|
||||
list.add(BYTE_STRING_B);
|
||||
list.add(BYTE_STRING_C);
|
||||
|
||||
assertEquals(3, list.size());
|
||||
assertSame(BYTE_STRING_A, list.getByteString(0));
|
||||
assertSame(BYTE_STRING_B, list.getByteString(1));
|
||||
assertSame(BYTE_STRING_C, list.getByteString(2));
|
||||
|
||||
list.remove(1);
|
||||
assertSame(BYTE_STRING_A, list.getByteString(0));
|
||||
assertSame(BYTE_STRING_C, list.getByteString(1));
|
||||
}
|
||||
|
||||
public void testConversionBackAndForth() {
|
||||
LazyStringArrayList list = new LazyStringArrayList();
|
||||
list.add(STRING_A);
|
||||
list.add(BYTE_STRING_B);
|
||||
list.add(BYTE_STRING_C);
|
||||
|
||||
// String a should be the same because it was originally a string
|
||||
assertSame(STRING_A, list.get(0));
|
||||
|
||||
// String b and c should be different because the string has to be computed
|
||||
// from the ByteString
|
||||
String bPrime = list.get(1);
|
||||
assertNotSame(STRING_B, bPrime);
|
||||
assertEquals(STRING_B, bPrime);
|
||||
String cPrime = list.get(2);
|
||||
assertNotSame(STRING_C, cPrime);
|
||||
assertEquals(STRING_C, cPrime);
|
||||
|
||||
// String c and c should stay the same once cached.
|
||||
assertSame(bPrime, list.get(1));
|
||||
assertSame(cPrime, list.get(2));
|
||||
|
||||
// ByteString needs to be computed from string for both a and b
|
||||
ByteString aPrimeByteString = list.getByteString(0);
|
||||
assertEquals(BYTE_STRING_A, aPrimeByteString);
|
||||
ByteString bPrimeByteString = list.getByteString(1);
|
||||
assertNotSame(BYTE_STRING_B, bPrimeByteString);
|
||||
assertEquals(BYTE_STRING_B, list.getByteString(1));
|
||||
|
||||
// Once cached, ByteString should stay cached.
|
||||
assertSame(aPrimeByteString, list.getByteString(0));
|
||||
assertSame(bPrimeByteString, list.getByteString(1));
|
||||
}
|
||||
|
||||
public void testCopyConstructorCopiesByReference() {
|
||||
LazyStringArrayList list1 = new LazyStringArrayList();
|
||||
list1.add(STRING_A);
|
||||
list1.add(BYTE_STRING_B);
|
||||
list1.add(BYTE_STRING_C);
|
||||
|
||||
LazyStringArrayList list2 = new LazyStringArrayList(list1);
|
||||
assertEquals(3, list2.size());
|
||||
assertSame(STRING_A, list2.get(0));
|
||||
assertSame(BYTE_STRING_B, list2.getByteString(1));
|
||||
assertSame(BYTE_STRING_C, list2.getByteString(2));
|
||||
}
|
||||
|
||||
public void testListCopyConstructor() {
|
||||
List<String> list1 = new ArrayList<String>();
|
||||
list1.add(STRING_A);
|
||||
list1.add(STRING_B);
|
||||
list1.add(STRING_C);
|
||||
|
||||
LazyStringArrayList list2 = new LazyStringArrayList(list1);
|
||||
assertEquals(3, list2.size());
|
||||
assertSame(STRING_A, list2.get(0));
|
||||
assertSame(STRING_B, list2.get(1));
|
||||
assertSame(STRING_C, list2.get(2));
|
||||
}
|
||||
|
||||
public void testAddAllCopiesByReferenceIfPossible() {
|
||||
LazyStringArrayList list1 = new LazyStringArrayList();
|
||||
list1.add(STRING_A);
|
||||
list1.add(BYTE_STRING_B);
|
||||
list1.add(BYTE_STRING_C);
|
||||
|
||||
LazyStringArrayList list2 = new LazyStringArrayList();
|
||||
list2.addAll(list1);
|
||||
|
||||
assertEquals(3, list2.size());
|
||||
assertSame(STRING_A, list2.get(0));
|
||||
assertSame(BYTE_STRING_B, list2.getByteString(1));
|
||||
assertSame(BYTE_STRING_C, list2.getByteString(2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
|
||||
import protobuf_unittest.UnittestProto;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tests to make sure the lazy conversion of UTF8-encoded byte arrays to
|
||||
* strings works correctly.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class LazyStringEndToEndTest extends TestCase {
|
||||
|
||||
private static ByteString TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8 =
|
||||
ByteString.copyFrom(new byte[] {
|
||||
114, 4, -1, 0, -1, 0, -30, 2, 4, -1,
|
||||
0, -1, 0, -30, 2, 4, -1, 0, -1, 0, });
|
||||
|
||||
private ByteString encodedTestAllTypes;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
this.encodedTestAllTypes = UnittestProto.TestAllTypes.newBuilder()
|
||||
.setOptionalString("foo")
|
||||
.addRepeatedString("bar")
|
||||
.addRepeatedString("baz")
|
||||
.build()
|
||||
.toByteString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an invalid UTF8 string will roundtrip through a parse
|
||||
* and serialization.
|
||||
*/
|
||||
public void testParseAndSerialize() throws InvalidProtocolBufferException {
|
||||
UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom(
|
||||
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8);
|
||||
ByteString bytes = tV2.toByteString();
|
||||
assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes);
|
||||
|
||||
tV2.getOptionalString();
|
||||
bytes = tV2.toByteString();
|
||||
assertEquals(TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8, bytes);
|
||||
}
|
||||
|
||||
public void testParseAndWrite() throws IOException {
|
||||
UnittestProto.TestAllTypes tV2 = UnittestProto.TestAllTypes.parseFrom(
|
||||
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8);
|
||||
byte[] sink = new byte[TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8.size()];
|
||||
CodedOutputStream outputStream = CodedOutputStream.newInstance(sink);
|
||||
tV2.writeTo(outputStream);
|
||||
outputStream.flush();
|
||||
assertEquals(
|
||||
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8,
|
||||
ByteString.copyFrom(sink));
|
||||
}
|
||||
|
||||
public void testCaching() {
|
||||
String a = "a";
|
||||
String b = "b";
|
||||
String c = "c";
|
||||
UnittestProto.TestAllTypes proto = UnittestProto.TestAllTypes.newBuilder()
|
||||
.setOptionalString(a)
|
||||
.addRepeatedString(b)
|
||||
.addRepeatedString(c)
|
||||
.build();
|
||||
|
||||
// String should be the one we passed it.
|
||||
assertSame(a, proto.getOptionalString());
|
||||
assertSame(b, proto.getRepeatedString(0));
|
||||
assertSame(c, proto.getRepeatedString(1));
|
||||
|
||||
|
||||
// There's no way to directly observe that the ByteString is cached
|
||||
// correctly on serialization, but we can observe that it had to recompute
|
||||
// the string after serialization.
|
||||
proto.toByteString();
|
||||
String aPrime = proto.getOptionalString();
|
||||
assertNotSame(a, aPrime);
|
||||
assertEquals(a, aPrime);
|
||||
String bPrime = proto.getRepeatedString(0);
|
||||
assertNotSame(b, bPrime);
|
||||
assertEquals(b, bPrime);
|
||||
String cPrime = proto.getRepeatedString(1);
|
||||
assertNotSame(c, cPrime);
|
||||
assertEquals(c, cPrime);
|
||||
|
||||
// And now the string should stay cached.
|
||||
assertSame(aPrime, proto.getOptionalString());
|
||||
assertSame(bPrime, proto.getRepeatedString(0));
|
||||
assertSame(cPrime, proto.getRepeatedString(1));
|
||||
}
|
||||
|
||||
public void testNoStringCachingIfOnlyBytesAccessed() throws Exception {
|
||||
UnittestProto.TestAllTypes proto =
|
||||
UnittestProto.TestAllTypes.parseFrom(encodedTestAllTypes);
|
||||
ByteString optional = proto.getOptionalStringBytes();
|
||||
assertSame(optional, proto.getOptionalStringBytes());
|
||||
assertSame(optional, proto.toBuilder().getOptionalStringBytes());
|
||||
|
||||
ByteString repeated0 = proto.getRepeatedStringBytes(0);
|
||||
ByteString repeated1 = proto.getRepeatedStringBytes(1);
|
||||
assertSame(repeated0, proto.getRepeatedStringBytes(0));
|
||||
assertSame(repeated1, proto.getRepeatedStringBytes(1));
|
||||
assertSame(repeated0, proto.toBuilder().getRepeatedStringBytes(0));
|
||||
assertSame(repeated1, proto.toBuilder().getRepeatedStringBytes(1));
|
||||
}
|
||||
}
|
||||
148
java/src/test/java/com/google/protobuf/LiteTest.java
Normal file
148
java/src/test/java/com/google/protobuf/LiteTest.java
Normal file
@@ -0,0 +1,148 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.UnittestLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* Test lite runtime.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class LiteTest extends TestCase {
|
||||
public void setUp() throws Exception {
|
||||
// Test that nested extensions are initialized correctly even if the outer
|
||||
// class has not been accessed directly. This was once a bug with lite
|
||||
// messages.
|
||||
//
|
||||
// We put this in setUp() rather than in its own test method because we
|
||||
// need to make sure it runs before any actual tests.
|
||||
assertTrue(TestNestedExtensionLite.nestedExtension != null);
|
||||
}
|
||||
|
||||
public void testLite() throws Exception {
|
||||
// Since lite messages are a subset of regular messages, we can mostly
|
||||
// assume that the functionality of lite messages is already thoroughly
|
||||
// tested by the regular tests. All this test really verifies is that
|
||||
// a proto with optimize_for = LITE_RUNTIME compiles correctly when
|
||||
// linked only against the lite library. That is all tested at compile
|
||||
// time, leaving not much to do in this method. Let's just do some random
|
||||
// stuff to make sure the lite message is actually here and usable.
|
||||
|
||||
TestAllTypesLite message =
|
||||
TestAllTypesLite.newBuilder()
|
||||
.setOptionalInt32(123)
|
||||
.addRepeatedString("hello")
|
||||
.setOptionalNestedMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.build();
|
||||
|
||||
ByteString data = message.toByteString();
|
||||
|
||||
TestAllTypesLite message2 = TestAllTypesLite.parseFrom(data);
|
||||
|
||||
assertEquals(123, message2.getOptionalInt32());
|
||||
assertEquals(1, message2.getRepeatedStringCount());
|
||||
assertEquals("hello", message2.getRepeatedString(0));
|
||||
assertEquals(7, message2.getOptionalNestedMessage().getBb());
|
||||
}
|
||||
|
||||
public void testLiteExtensions() throws Exception {
|
||||
// TODO(kenton): Unlike other features of the lite library, extensions are
|
||||
// implemented completely differently from the regular library. We
|
||||
// should probably test them more thoroughly.
|
||||
|
||||
TestAllExtensionsLite message =
|
||||
TestAllExtensionsLite.newBuilder()
|
||||
.setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
|
||||
.addExtension(UnittestLite.repeatedStringExtensionLite, "hello")
|
||||
.setExtension(UnittestLite.optionalNestedEnumExtensionLite,
|
||||
TestAllTypesLite.NestedEnum.BAZ)
|
||||
.setExtension(UnittestLite.optionalNestedMessageExtensionLite,
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
|
||||
.build();
|
||||
|
||||
// Test copying a message, since coping extensions actually does use a
|
||||
// different code path between lite and regular libraries, and as of this
|
||||
// writing, parsing hasn't been implemented yet.
|
||||
TestAllExtensionsLite message2 = message.toBuilder().build();
|
||||
|
||||
assertEquals(123, (int) message2.getExtension(
|
||||
UnittestLite.optionalInt32ExtensionLite));
|
||||
assertEquals(1, message2.getExtensionCount(
|
||||
UnittestLite.repeatedStringExtensionLite));
|
||||
assertEquals(1, message2.getExtension(
|
||||
UnittestLite.repeatedStringExtensionLite).size());
|
||||
assertEquals("hello", message2.getExtension(
|
||||
UnittestLite.repeatedStringExtensionLite, 0));
|
||||
assertEquals(TestAllTypesLite.NestedEnum.BAZ, message2.getExtension(
|
||||
UnittestLite.optionalNestedEnumExtensionLite));
|
||||
assertEquals(7, message2.getExtension(
|
||||
UnittestLite.optionalNestedMessageExtensionLite).getBb());
|
||||
}
|
||||
|
||||
public void testSerialize() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TestAllTypesLite expected =
|
||||
TestAllTypesLite.newBuilder()
|
||||
.setOptionalInt32(123)
|
||||
.addRepeatedString("hello")
|
||||
.setOptionalNestedMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.build();
|
||||
ObjectOutputStream out = new ObjectOutputStream(baos);
|
||||
try {
|
||||
out.writeObject(expected);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
TestAllTypesLite actual = (TestAllTypesLite) in.readObject();
|
||||
assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32());
|
||||
assertEquals(expected.getRepeatedStringCount(),
|
||||
actual.getRepeatedStringCount());
|
||||
assertEquals(expected.getRepeatedString(0),
|
||||
actual.getRepeatedString(0));
|
||||
assertEquals(expected.getOptionalNestedMessage().getBb(),
|
||||
actual.getOptionalNestedMessage().getBb());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Test {@link LiteralByteString} by setting up a reference string in {@link #setUp()}.
|
||||
* This class is designed to be extended for testing extensions of {@link LiteralByteString}
|
||||
* such as {@link BoundedByteString}, see {@link BoundedByteStringTest}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class LiteralByteStringTest extends TestCase {
|
||||
protected static final String UTF_8 = "UTF-8";
|
||||
|
||||
protected String classUnderTest;
|
||||
protected byte[] referenceBytes;
|
||||
protected ByteString stringUnderTest;
|
||||
protected int expectedHashCode;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "LiteralByteString";
|
||||
referenceBytes = ByteStringTest.getTestBytes(1234, 11337766L);
|
||||
stringUnderTest = ByteString.copyFrom(referenceBytes);
|
||||
expectedHashCode = 331161852;
|
||||
}
|
||||
|
||||
public void testExpectedType() {
|
||||
String actualClassName = getActualClassName(stringUnderTest);
|
||||
assertEquals(classUnderTest + " should match type exactly", classUnderTest, actualClassName);
|
||||
}
|
||||
|
||||
protected String getActualClassName(Object object) {
|
||||
String actualClassName = object.getClass().getName();
|
||||
actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1);
|
||||
return actualClassName;
|
||||
}
|
||||
|
||||
public void testByteAt() {
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < referenceBytes.length; ++i) {
|
||||
stillEqual = (referenceBytes[i] == stringUnderTest.byteAt(i));
|
||||
}
|
||||
assertTrue(classUnderTest + " must capture the right bytes", stillEqual);
|
||||
}
|
||||
|
||||
public void testByteIterator() {
|
||||
boolean stillEqual = true;
|
||||
ByteString.ByteIterator iter = stringUnderTest.iterator();
|
||||
for (int i = 0; stillEqual && i < referenceBytes.length; ++i) {
|
||||
stillEqual = (iter.hasNext() && referenceBytes[i] == iter.nextByte());
|
||||
}
|
||||
assertTrue(classUnderTest + " must capture the right bytes", stillEqual);
|
||||
assertFalse(classUnderTest + " must have exhausted the itertor", iter.hasNext());
|
||||
|
||||
try {
|
||||
iter.nextByte();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NoSuchElementException e) {
|
||||
// This is success
|
||||
}
|
||||
}
|
||||
|
||||
public void testByteIterable() {
|
||||
boolean stillEqual = true;
|
||||
int j = 0;
|
||||
for (byte quantum : stringUnderTest) {
|
||||
stillEqual = (referenceBytes[j] == quantum);
|
||||
++j;
|
||||
}
|
||||
assertTrue(classUnderTest + " must capture the right bytes as Bytes", stillEqual);
|
||||
assertEquals(classUnderTest + " iterable character count", referenceBytes.length, j);
|
||||
}
|
||||
|
||||
public void testSize() {
|
||||
assertEquals(classUnderTest + " must have the expected size", referenceBytes.length,
|
||||
stringUnderTest.size());
|
||||
}
|
||||
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(classUnderTest + " must have depth 0", 0, stringUnderTest.getTreeDepth());
|
||||
}
|
||||
|
||||
public void testIsBalanced() {
|
||||
assertTrue(classUnderTest + " is technically balanced", stringUnderTest.isBalanced());
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteArrayOffsetLength() {
|
||||
int destinationOffset = 50;
|
||||
int length = 100;
|
||||
byte[] destination = new byte[destinationOffset + length];
|
||||
int sourceOffset = 213;
|
||||
stringUnderTest.copyTo(destination, sourceOffset, destinationOffset, length);
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < length; ++i) {
|
||||
stillEqual = referenceBytes[i + sourceOffset] == destination[i + destinationOffset];
|
||||
}
|
||||
assertTrue(classUnderTest + ".copyTo(4 arg) must give the expected bytes", stillEqual);
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteArrayOffsetLengthErrors() {
|
||||
int destinationOffset = 50;
|
||||
int length = 100;
|
||||
byte[] destination = new byte[destinationOffset + length];
|
||||
|
||||
try {
|
||||
// Copy one too many bytes
|
||||
stringUnderTest.copyTo(destination, stringUnderTest.size() + 1 - length,
|
||||
destinationOffset, length);
|
||||
fail("Should have thrown an exception when copying too many bytes of a "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal negative sourceOffset
|
||||
stringUnderTest.copyTo(destination, -1, destinationOffset, length);
|
||||
fail("Should have thrown an exception when given a negative sourceOffset in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal negative destinationOffset
|
||||
stringUnderTest.copyTo(destination, 0, -1, length);
|
||||
fail("Should have thrown an exception when given a negative destinationOffset in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal negative size
|
||||
stringUnderTest.copyTo(destination, 0, 0, -1);
|
||||
fail("Should have thrown an exception when given a negative size in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal too-large sourceOffset
|
||||
stringUnderTest.copyTo(destination, 2 * stringUnderTest.size(), 0, length);
|
||||
fail("Should have thrown an exception when the destinationOffset is too large in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy with illegal too-large destinationOffset
|
||||
stringUnderTest.copyTo(destination, 0, 2 * destination.length, length);
|
||||
fail("Should have thrown an exception when the destinationOffset is too large in "
|
||||
+ classUnderTest);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
// This is success
|
||||
}
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteBuffer() {
|
||||
ByteBuffer myBuffer = ByteBuffer.allocate(referenceBytes.length);
|
||||
stringUnderTest.copyTo(myBuffer);
|
||||
assertTrue(classUnderTest + ".copyTo(ByteBuffer) must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, myBuffer.array()));
|
||||
}
|
||||
|
||||
public void testAsReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer = stringUnderTest.asReadOnlyByteBuffer();
|
||||
byte[] roundTripBytes = new byte[referenceBytes.length];
|
||||
assertTrue(byteBuffer.remaining() == referenceBytes.length);
|
||||
assertTrue(byteBuffer.isReadOnly());
|
||||
byteBuffer.get(roundTripBytes);
|
||||
assertTrue(classUnderTest + ".asReadOnlyByteBuffer() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testAsReadOnlyByteBufferList() {
|
||||
List<ByteBuffer> byteBuffers = stringUnderTest.asReadOnlyByteBufferList();
|
||||
int bytesSeen = 0;
|
||||
byte[] roundTripBytes = new byte[referenceBytes.length];
|
||||
for (ByteBuffer byteBuffer : byteBuffers) {
|
||||
int thisLength = byteBuffer.remaining();
|
||||
assertTrue(byteBuffer.isReadOnly());
|
||||
assertTrue(bytesSeen + thisLength <= referenceBytes.length);
|
||||
byteBuffer.get(roundTripBytes, bytesSeen, thisLength);
|
||||
bytesSeen += thisLength;
|
||||
}
|
||||
assertTrue(bytesSeen == referenceBytes.length);
|
||||
assertTrue(classUnderTest + ".asReadOnlyByteBufferTest() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testToByteArray() {
|
||||
byte[] roundTripBytes = stringUnderTest.toByteArray();
|
||||
assertTrue(classUnderTest + ".toByteArray() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteTo() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
stringUnderTest.writeTo(bos);
|
||||
byte[] roundTripBytes = bos.toByteArray();
|
||||
assertTrue(classUnderTest + ".writeTo() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteTo_mutating() throws IOException {
|
||||
OutputStream os = new OutputStream() {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
for (int x = 0; x < len; ++x) {
|
||||
b[off + x] = (byte) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
// Purposefully left blank.
|
||||
}
|
||||
};
|
||||
|
||||
stringUnderTest.writeTo(os);
|
||||
byte[] newBytes = stringUnderTest.toByteArray();
|
||||
assertTrue(classUnderTest + ".writeTo() must not grant access to underlying array",
|
||||
Arrays.equals(referenceBytes, newBytes));
|
||||
}
|
||||
|
||||
public void testNewOutput() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ByteString.Output output = ByteString.newOutput();
|
||||
stringUnderTest.writeTo(output);
|
||||
assertEquals("Output Size returns correct result",
|
||||
output.size(), stringUnderTest.size());
|
||||
output.writeTo(bos);
|
||||
assertTrue("Output.writeTo() must give back the same bytes",
|
||||
Arrays.equals(referenceBytes, bos.toByteArray()));
|
||||
|
||||
// write the output stream to itself! This should cause it to double
|
||||
output.writeTo(output);
|
||||
assertEquals("Writing an output stream to itself is successful",
|
||||
stringUnderTest.concat(stringUnderTest), output.toByteString());
|
||||
|
||||
output.reset();
|
||||
assertEquals("Output.reset() resets the output", 0, output.size());
|
||||
assertEquals("Output.reset() resets the output",
|
||||
ByteString.EMPTY, output.toByteString());
|
||||
|
||||
}
|
||||
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String testString = "I love unicode \u1234\u5678 characters";
|
||||
LiteralByteString unicode = new LiteralByteString(testString.getBytes(UTF_8));
|
||||
String roundTripString = unicode.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
|
||||
}
|
||||
|
||||
public void testEquals() {
|
||||
assertEquals(classUnderTest + " must not equal null", false, stringUnderTest.equals(null));
|
||||
assertEquals(classUnderTest + " must equal self", stringUnderTest, stringUnderTest);
|
||||
assertFalse(classUnderTest + " must not equal the empty string",
|
||||
stringUnderTest.equals(ByteString.EMPTY));
|
||||
assertEquals(classUnderTest + " empty strings must be equal",
|
||||
new LiteralByteString(new byte[]{}), stringUnderTest.substring(55, 55));
|
||||
assertEquals(classUnderTest + " must equal another string with the same value",
|
||||
stringUnderTest, new LiteralByteString(referenceBytes));
|
||||
|
||||
byte[] mungedBytes = new byte[referenceBytes.length];
|
||||
System.arraycopy(referenceBytes, 0, mungedBytes, 0, referenceBytes.length);
|
||||
mungedBytes[mungedBytes.length - 5] ^= 0xFF;
|
||||
assertFalse(classUnderTest + " must not equal every string with the same length",
|
||||
stringUnderTest.equals(new LiteralByteString(mungedBytes)));
|
||||
}
|
||||
|
||||
public void testHashCode() {
|
||||
int hash = stringUnderTest.hashCode();
|
||||
assertEquals(classUnderTest + " must have expected hashCode", expectedHashCode, hash);
|
||||
}
|
||||
|
||||
public void testPeekCachedHashCode() {
|
||||
assertEquals(classUnderTest + ".peekCachedHashCode() should return zero at first", 0,
|
||||
stringUnderTest.peekCachedHashCode());
|
||||
stringUnderTest.hashCode();
|
||||
assertEquals(classUnderTest + ".peekCachedHashCode should return zero at first",
|
||||
expectedHashCode, stringUnderTest.peekCachedHashCode());
|
||||
}
|
||||
|
||||
public void testPartialHash() {
|
||||
// partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
|
||||
// This test would fail if the expected hash were 1. It's not.
|
||||
int hash = stringUnderTest.partialHash(stringUnderTest.size(), 0, stringUnderTest.size());
|
||||
assertEquals(classUnderTest + ".partialHash() must yield expected hashCode",
|
||||
expectedHashCode, hash);
|
||||
}
|
||||
|
||||
public void testNewInput() throws IOException {
|
||||
InputStream input = stringUnderTest.newInput();
|
||||
assertEquals("InputStream.available() returns correct value",
|
||||
stringUnderTest.size(), input.available());
|
||||
boolean stillEqual = true;
|
||||
for (byte referenceByte : referenceBytes) {
|
||||
int expectedInt = (referenceByte & 0xFF);
|
||||
stillEqual = (expectedInt == input.read());
|
||||
}
|
||||
assertEquals("InputStream.available() returns correct value",
|
||||
0, input.available());
|
||||
assertTrue(classUnderTest + " must give the same bytes from the InputStream", stillEqual);
|
||||
assertEquals(classUnderTest + " InputStream must now be exhausted", -1, input.read());
|
||||
}
|
||||
|
||||
public void testNewInput_skip() throws IOException {
|
||||
InputStream input = stringUnderTest.newInput();
|
||||
int stringSize = stringUnderTest.size();
|
||||
int nearEndIndex = stringSize * 2 / 3;
|
||||
long skipped1 = input.skip(nearEndIndex);
|
||||
assertEquals("InputStream.skip()", skipped1, nearEndIndex);
|
||||
assertEquals("InputStream.available()",
|
||||
stringSize - skipped1, input.available());
|
||||
assertTrue("InputStream.mark() is available", input.markSupported());
|
||||
input.mark(0);
|
||||
assertEquals("InputStream.skip(), read()",
|
||||
stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
assertEquals("InputStream.available()",
|
||||
stringSize - skipped1 - 1, input.available());
|
||||
long skipped2 = input.skip(stringSize);
|
||||
assertEquals("InputStream.skip() incomplete",
|
||||
skipped2, stringSize - skipped1 - 1);
|
||||
assertEquals("InputStream.skip(), no more input", 0, input.available());
|
||||
assertEquals("InputStream.skip(), no more input", -1, input.read());
|
||||
input.reset();
|
||||
assertEquals("InputStream.reset() succeded",
|
||||
stringSize - skipped1, input.available());
|
||||
assertEquals("InputStream.reset(), read()",
|
||||
stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
}
|
||||
|
||||
public void testNewCodedInput() throws IOException {
|
||||
CodedInputStream cis = stringUnderTest.newCodedInput();
|
||||
byte[] roundTripBytes = cis.readRawBytes(referenceBytes.length);
|
||||
assertTrue(classUnderTest + " must give the same bytes back from the CodedInputStream",
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
assertTrue(classUnderTest + " CodedInputStream must now be exhausted", cis.isAtEnd());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we keep things simple when concatenating with empty. See also
|
||||
* {@link ByteStringTest#testConcat_empty()}.
|
||||
*/
|
||||
public void testConcat_empty() {
|
||||
assertSame(classUnderTest + " concatenated with empty must give " + classUnderTest,
|
||||
stringUnderTest.concat(ByteString.EMPTY), stringUnderTest);
|
||||
assertSame("empty concatenated with " + classUnderTest + " must give " + classUnderTest,
|
||||
ByteString.EMPTY.concat(stringUnderTest), stringUnderTest);
|
||||
}
|
||||
}
|
||||
353
java/src/test/java/com/google/protobuf/MessageTest.java
Normal file
353
java/src/test/java/com/google/protobuf/MessageTest.java
Normal file
@@ -0,0 +1,353 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredForeign;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Misc. unit tests for message operations that apply to both generated
|
||||
* and dynamic messages.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class MessageTest extends TestCase {
|
||||
// =================================================================
|
||||
// Message-merging tests.
|
||||
|
||||
static final TestAllTypes MERGE_SOURCE =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalString("foo")
|
||||
.setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedString("bar")
|
||||
.build();
|
||||
|
||||
static final TestAllTypes MERGE_DEST =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt64(2)
|
||||
.setOptionalString("baz")
|
||||
.setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build())
|
||||
.addRepeatedString("qux")
|
||||
.build();
|
||||
|
||||
static final String MERGE_RESULT_TEXT =
|
||||
"optional_int32: 1\n" +
|
||||
"optional_int64: 2\n" +
|
||||
"optional_string: \"foo\"\n" +
|
||||
"optional_foreign_message {\n" +
|
||||
" c: 3\n" +
|
||||
"}\n" +
|
||||
"repeated_string: \"qux\"\n" +
|
||||
"repeated_string: \"bar\"\n";
|
||||
|
||||
public void testMergeFrom() throws Exception {
|
||||
TestAllTypes result =
|
||||
TestAllTypes.newBuilder(MERGE_DEST)
|
||||
.mergeFrom(MERGE_SOURCE).build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test merging a DynamicMessage into a GeneratedMessage. As long as they
|
||||
* have the same descriptor, this should work, but it is an entirely different
|
||||
* code path.
|
||||
*/
|
||||
public void testMergeFromDynamic() throws Exception {
|
||||
TestAllTypes result =
|
||||
TestAllTypes.newBuilder(MERGE_DEST)
|
||||
.mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build())
|
||||
.build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
/** Test merging two DynamicMessages. */
|
||||
public void testDynamicMergeFrom() throws Exception {
|
||||
DynamicMessage result =
|
||||
DynamicMessage.newBuilder(MERGE_DEST)
|
||||
.mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build())
|
||||
.build();
|
||||
|
||||
assertEquals(MERGE_RESULT_TEXT, result.toString());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Required-field-related tests.
|
||||
|
||||
private static final TestRequired TEST_REQUIRED_UNINITIALIZED =
|
||||
TestRequired.getDefaultInstance();
|
||||
private static final TestRequired TEST_REQUIRED_INITIALIZED =
|
||||
TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
|
||||
|
||||
public void testRequired() throws Exception {
|
||||
TestRequired.Builder builder = TestRequired.newBuilder();
|
||||
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setA(1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setB(1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setC(1);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredForeign() throws Exception {
|
||||
TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
|
||||
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredExtension() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.setExtension(TestRequired.single, TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setExtension(TestRequired.single, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.addExtension(TestRequired.multi, TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setExtension(TestRequired.multi, 0, TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredDynamic() throws Exception {
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor);
|
||||
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setField(descriptor.findFieldByName("a"), 1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setField(descriptor.findFieldByName("b"), 1);
|
||||
assertFalse(builder.isInitialized());
|
||||
builder.setField(descriptor.findFieldByName("c"), 1);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testRequiredDynamicForeign() throws Exception {
|
||||
Descriptors.Descriptor descriptor = TestRequiredForeign.getDescriptor();
|
||||
DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor);
|
||||
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.setField(descriptor.findFieldByName("optional_message"),
|
||||
TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setField(descriptor.findFieldByName("optional_message"),
|
||||
TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
|
||||
builder.addRepeatedField(descriptor.findFieldByName("repeated_message"),
|
||||
TEST_REQUIRED_UNINITIALIZED);
|
||||
assertFalse(builder.isInitialized());
|
||||
|
||||
builder.setRepeatedField(descriptor.findFieldByName("repeated_message"), 0,
|
||||
TEST_REQUIRED_INITIALIZED);
|
||||
assertTrue(builder.isInitialized());
|
||||
}
|
||||
|
||||
public void testUninitializedException() throws Exception {
|
||||
try {
|
||||
TestRequired.newBuilder().build();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UninitializedMessageException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testBuildPartial() throws Exception {
|
||||
// We're mostly testing that no exception is thrown.
|
||||
TestRequired message = TestRequired.newBuilder().buildPartial();
|
||||
assertFalse(message.isInitialized());
|
||||
}
|
||||
|
||||
public void testNestedUninitializedException() throws Exception {
|
||||
try {
|
||||
TestRequiredForeign.newBuilder()
|
||||
.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.build();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UninitializedMessageException e) {
|
||||
assertEquals(
|
||||
"Message missing required fields: " +
|
||||
"optional_message.a, " +
|
||||
"optional_message.b, " +
|
||||
"optional_message.c, " +
|
||||
"repeated_message[0].a, " +
|
||||
"repeated_message[0].b, " +
|
||||
"repeated_message[0].c, " +
|
||||
"repeated_message[1].a, " +
|
||||
"repeated_message[1].b, " +
|
||||
"repeated_message[1].c",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testBuildNestedPartial() throws Exception {
|
||||
// We're mostly testing that no exception is thrown.
|
||||
TestRequiredForeign message =
|
||||
TestRequiredForeign.newBuilder()
|
||||
.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.buildPartial();
|
||||
assertFalse(message.isInitialized());
|
||||
}
|
||||
|
||||
public void testParseUnititialized() throws Exception {
|
||||
try {
|
||||
TestRequired.parseFrom(ByteString.EMPTY);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseNestedUnititialized() throws Exception {
|
||||
ByteString data =
|
||||
TestRequiredForeign.newBuilder()
|
||||
.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED)
|
||||
.buildPartial().toByteString();
|
||||
|
||||
try {
|
||||
TestRequiredForeign.parseFrom(data);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals(
|
||||
"Message missing required fields: " +
|
||||
"optional_message.a, " +
|
||||
"optional_message.b, " +
|
||||
"optional_message.c, " +
|
||||
"repeated_message[0].a, " +
|
||||
"repeated_message[0].b, " +
|
||||
"repeated_message[0].c, " +
|
||||
"repeated_message[1].a, " +
|
||||
"repeated_message[1].b, " +
|
||||
"repeated_message[1].c",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDynamicUninitializedException() throws Exception {
|
||||
try {
|
||||
DynamicMessage.newBuilder(TestRequired.getDescriptor()).build();
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UninitializedMessageException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testDynamicBuildPartial() throws Exception {
|
||||
// We're mostly testing that no exception is thrown.
|
||||
DynamicMessage message =
|
||||
DynamicMessage.newBuilder(TestRequired.getDescriptor())
|
||||
.buildPartial();
|
||||
assertFalse(message.isInitialized());
|
||||
}
|
||||
|
||||
public void testDynamicParseUnititialized() throws Exception {
|
||||
try {
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage.parseFrom(descriptor, ByteString.EMPTY);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
assertEquals("Message missing required fields: a, b, c", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Test reading unset repeated message from DynamicMessage. */
|
||||
public void testDynamicRepeatedMessageNull() throws Exception {
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage result =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor())
|
||||
.mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build())
|
||||
.build();
|
||||
|
||||
assertTrue(result.getField(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")) instanceof List<?>);
|
||||
assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")), 0);
|
||||
}
|
||||
|
||||
/** Test reading repeated message from DynamicMessage. */
|
||||
public void testDynamicRepeatedMessageNotNull() throws Exception {
|
||||
|
||||
TestAllTypes REPEATED_NESTED =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalString("foo")
|
||||
.setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedString("bar")
|
||||
.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance())
|
||||
.build();
|
||||
Descriptors.Descriptor descriptor = TestRequired.getDescriptor();
|
||||
DynamicMessage result =
|
||||
DynamicMessage.newBuilder(TestAllTypes.getDescriptor())
|
||||
.mergeFrom(DynamicMessage.newBuilder(REPEATED_NESTED).build())
|
||||
.build();
|
||||
|
||||
assertTrue(result.getField(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")) instanceof List<?>);
|
||||
assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
|
||||
.findFieldByName("repeated_foreign_message")), 2);
|
||||
}
|
||||
}
|
||||
185
java/src/test/java/com/google/protobuf/NestedBuildersTest.java
Normal file
185
java/src/test/java/com/google/protobuf/NestedBuildersTest.java
Normal file
@@ -0,0 +1,185 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.Vehicle;
|
||||
import protobuf_unittest.Wheel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Test cases that exercise end-to-end use cases involving
|
||||
* {@link SingleFieldBuilder} and {@link RepeatedFieldBuilder}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class NestedBuildersTest extends TestCase {
|
||||
|
||||
public void testMessagesAndBuilders() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(1);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(2);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(3);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(4)
|
||||
.setWidth(4);
|
||||
vehicleBuilder.getEngineBuilder()
|
||||
.setLiters(10);
|
||||
|
||||
Vehicle vehicle = vehicleBuilder.build();
|
||||
assertEquals(4, vehicle.getWheelCount());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Wheel wheel = vehicle.getWheel(i);
|
||||
assertEquals(4, wheel.getRadius());
|
||||
assertEquals(i + 1, wheel.getWidth());
|
||||
}
|
||||
assertEquals(10, vehicle.getEngine().getLiters());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vehicleBuilder.getWheelBuilder(i)
|
||||
.setRadius(5)
|
||||
.setWidth(i + 10);
|
||||
}
|
||||
vehicleBuilder.getEngineBuilder().setLiters(20);
|
||||
|
||||
vehicle = vehicleBuilder.build();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Wheel wheel = vehicle.getWheel(i);
|
||||
assertEquals(5, wheel.getRadius());
|
||||
assertEquals(i + 10, wheel.getWidth());
|
||||
}
|
||||
assertEquals(20, vehicle.getEngine().getLiters());
|
||||
assertTrue(vehicle.hasEngine());
|
||||
}
|
||||
|
||||
public void testMessagesAreCached() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(1)
|
||||
.setWidth(2);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(3)
|
||||
.setWidth(4);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(5)
|
||||
.setWidth(6);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(7)
|
||||
.setWidth(8);
|
||||
|
||||
// Make sure messages are cached.
|
||||
List<Wheel> wheels = new ArrayList<Wheel>(vehicleBuilder.getWheelList());
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
}
|
||||
|
||||
// Now get builders and check they didn't change.
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
vehicleBuilder.getWheel(i);
|
||||
}
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
}
|
||||
|
||||
// Change just one
|
||||
vehicleBuilder.getWheelBuilder(3)
|
||||
.setRadius(20).setWidth(20);
|
||||
|
||||
// Now get wheels and check that only that one changed
|
||||
for (int i = 0; i < wheels.size(); i++) {
|
||||
if (i < 3) {
|
||||
assertSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
} else {
|
||||
assertNotSame(wheels.get(i), vehicleBuilder.getWheel(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemove_WithNestedBuilders() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(1)
|
||||
.setWidth(1);
|
||||
vehicleBuilder.addWheelBuilder()
|
||||
.setRadius(2)
|
||||
.setWidth(2);
|
||||
vehicleBuilder.removeWheel(0);
|
||||
|
||||
assertEquals(1, vehicleBuilder.getWheelCount());
|
||||
assertEquals(2, vehicleBuilder.getWheel(0).getRadius());
|
||||
}
|
||||
|
||||
public void testRemove_WithNestedMessages() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.addWheel(Wheel.newBuilder()
|
||||
.setRadius(1)
|
||||
.setWidth(1));
|
||||
vehicleBuilder.addWheel(Wheel.newBuilder()
|
||||
.setRadius(2)
|
||||
.setWidth(2));
|
||||
vehicleBuilder.removeWheel(0);
|
||||
|
||||
assertEquals(1, vehicleBuilder.getWheelCount());
|
||||
assertEquals(2, vehicleBuilder.getWheel(0).getRadius());
|
||||
}
|
||||
|
||||
public void testMerge() {
|
||||
Vehicle vehicle1 = Vehicle.newBuilder()
|
||||
.addWheel(Wheel.newBuilder().setRadius(1).build())
|
||||
.addWheel(Wheel.newBuilder().setRadius(2).build())
|
||||
.build();
|
||||
|
||||
Vehicle vehicle2 = Vehicle.newBuilder()
|
||||
.mergeFrom(vehicle1)
|
||||
.build();
|
||||
// List should be the same -- no allocation
|
||||
assertSame(vehicle1.getWheelList(), vehicle2.getWheelList());
|
||||
|
||||
Vehicle vehicle3 = vehicle1.toBuilder().build();
|
||||
assertSame(vehicle1.getWheelList(), vehicle3.getWheelList());
|
||||
}
|
||||
|
||||
public void testGettingBuilderMarksFieldAsHaving() {
|
||||
Vehicle.Builder vehicleBuilder = Vehicle.newBuilder();
|
||||
vehicleBuilder.getEngineBuilder();
|
||||
Vehicle vehicle = vehicleBuilder.buildPartial();
|
||||
assertTrue(vehicle.hasEngine());
|
||||
}
|
||||
}
|
||||
375
java/src/test/java/com/google/protobuf/ParserTest.java
Normal file
375
java/src/test/java/com/google/protobuf/ParserTest.java
Normal file
@@ -0,0 +1,375 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestParsingMergeLite;
|
||||
import com.google.protobuf.UnittestLite;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestParsingMerge;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Parser}.
|
||||
*
|
||||
* @author liujisi@google.com (Pherl Liu)
|
||||
*/
|
||||
public class ParserTest extends TestCase {
|
||||
public void testGeneratedMessageParserSingleton() throws Exception {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(TestAllTypes.PARSER,
|
||||
TestUtil.getAllSet().getParserForType());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertRoundTripEquals(MessageLite message,
|
||||
ExtensionRegistryLite registry)
|
||||
throws Exception {
|
||||
final byte[] data = message.toByteArray();
|
||||
final int offset = 20;
|
||||
final int length = data.length;
|
||||
final int padding = 30;
|
||||
Parser<? extends MessageLite> parser = message.getParserForType();
|
||||
assertMessageEquals(message, parser.parseFrom(data, registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
generatePaddingArray(data, offset, padding),
|
||||
offset, length, registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
message.toByteString(), registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
new ByteArrayInputStream(data), registry));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
CodedInputStream.newInstance(data), registry));
|
||||
}
|
||||
|
||||
private void assertRoundTripEquals(MessageLite message) throws Exception {
|
||||
final byte[] data = message.toByteArray();
|
||||
final int offset = 20;
|
||||
final int length = data.length;
|
||||
final int padding = 30;
|
||||
Parser<? extends MessageLite> parser = message.getParserForType();
|
||||
assertMessageEquals(message, parser.parseFrom(data));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
generatePaddingArray(data, offset, padding),
|
||||
offset, length));
|
||||
assertMessageEquals(message, parser.parseFrom(message.toByteString()));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
new ByteArrayInputStream(data)));
|
||||
assertMessageEquals(message, parser.parseFrom(
|
||||
CodedInputStream.newInstance(data)));
|
||||
}
|
||||
|
||||
private void assertMessageEquals(MessageLite expected, MessageLite actual)
|
||||
throws Exception {
|
||||
if (expected instanceof Message) {
|
||||
assertEquals(expected, actual);
|
||||
} else {
|
||||
assertEquals(expected.toByteString(), actual.toByteString());
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] generatePaddingArray(byte[] data, int offset, int padding) {
|
||||
byte[] result = new byte[offset + data.length + padding];
|
||||
System.arraycopy(data, 0, result, offset, data.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testNormalMessage() throws Exception {
|
||||
assertRoundTripEquals(TestUtil.getAllSet());
|
||||
}
|
||||
|
||||
public void testParsePartial() throws Exception {
|
||||
Parser<TestRequired> parser = TestRequired.PARSER;
|
||||
final String errorString =
|
||||
"Should throw exceptions when the parsed message isn't initialized.";
|
||||
|
||||
// TestRequired.b and TestRequired.c are not set.
|
||||
TestRequired partialMessage = TestRequired.newBuilder()
|
||||
.setA(1).buildPartial();
|
||||
|
||||
// parsePartialFrom should pass.
|
||||
byte[] data = partialMessage.toByteArray();
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(data));
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(
|
||||
partialMessage.toByteString()));
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(
|
||||
new ByteArrayInputStream(data)));
|
||||
assertEquals(partialMessage, parser.parsePartialFrom(
|
||||
CodedInputStream.newInstance(data)));
|
||||
|
||||
// parseFrom(ByteArray)
|
||||
try {
|
||||
parser.parseFrom(partialMessage.toByteArray());
|
||||
fail(errorString);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// pass.
|
||||
}
|
||||
|
||||
// parseFrom(ByteString)
|
||||
try {
|
||||
parser.parseFrom(partialMessage.toByteString());
|
||||
fail(errorString);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// pass.
|
||||
}
|
||||
|
||||
// parseFrom(InputStream)
|
||||
try {
|
||||
parser.parseFrom(new ByteArrayInputStream(partialMessage.toByteArray()));
|
||||
fail(errorString);
|
||||
} catch (IOException e) {
|
||||
// pass.
|
||||
}
|
||||
|
||||
// parseFrom(CodedInputStream)
|
||||
try {
|
||||
parser.parseFrom(CodedInputStream.newInstance(
|
||||
partialMessage.toByteArray()));
|
||||
fail(errorString);
|
||||
} catch (IOException e) {
|
||||
// pass.
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseExtensions() throws Exception {
|
||||
assertRoundTripEquals(TestUtil.getAllExtensionsSet(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
assertRoundTripEquals(TestUtil.getAllLiteExtensionsSet(),
|
||||
TestUtil.getExtensionRegistryLite());
|
||||
}
|
||||
|
||||
public void testParsePacked() throws Exception {
|
||||
assertRoundTripEquals(TestUtil.getPackedSet());
|
||||
assertRoundTripEquals(TestUtil.getPackedExtensionsSet(),
|
||||
TestUtil.getExtensionRegistry());
|
||||
assertRoundTripEquals(TestUtil.getLitePackedExtensionsSet(),
|
||||
TestUtil.getExtensionRegistryLite());
|
||||
}
|
||||
|
||||
public void testParseDelimitedTo() throws Exception {
|
||||
// Write normal Message.
|
||||
TestAllTypes normalMessage = TestUtil.getAllSet();
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
normalMessage.writeDelimitedTo(output);
|
||||
|
||||
// Write MessageLite with packed extension fields.
|
||||
TestPackedExtensionsLite packedMessage =
|
||||
TestUtil.getLitePackedExtensionsSet();
|
||||
packedMessage.writeDelimitedTo(output);
|
||||
|
||||
InputStream input = new ByteArrayInputStream(output.toByteArray());
|
||||
assertMessageEquals(
|
||||
normalMessage,
|
||||
normalMessage.getParserForType().parseDelimitedFrom(input));
|
||||
assertMessageEquals(
|
||||
packedMessage,
|
||||
packedMessage.getParserForType().parseDelimitedFrom(
|
||||
input, TestUtil.getExtensionRegistryLite()));
|
||||
}
|
||||
|
||||
public void testParseUnknownFields() throws Exception {
|
||||
// All fields will be treated as unknown fields in emptyMessage.
|
||||
TestEmptyMessage emptyMessage = TestEmptyMessage.PARSER.parseFrom(
|
||||
TestUtil.getAllSet().toByteString());
|
||||
assertEquals(
|
||||
TestUtil.getAllSet().toByteString(),
|
||||
emptyMessage.toByteString());
|
||||
}
|
||||
|
||||
public void testOptimizeForSize() throws Exception {
|
||||
TestOptimizedForSize.Builder builder = TestOptimizedForSize.newBuilder();
|
||||
builder.setI(12).setMsg(ForeignMessage.newBuilder().setC(34).build());
|
||||
builder.setExtension(TestOptimizedForSize.testExtension, 56);
|
||||
builder.setExtension(TestOptimizedForSize.testExtension2,
|
||||
TestRequiredOptimizedForSize.newBuilder().setX(78).build());
|
||||
|
||||
TestOptimizedForSize message = builder.build();
|
||||
ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
UnittestOptimizeFor.registerAllExtensions(registry);
|
||||
|
||||
assertRoundTripEquals(message, registry);
|
||||
}
|
||||
|
||||
/** Helper method for {@link #testParsingMerge()}.*/
|
||||
private void assertMessageMerged(TestAllTypes allTypes)
|
||||
throws Exception {
|
||||
assertEquals(3, allTypes.getOptionalInt32());
|
||||
assertEquals(2, allTypes.getOptionalInt64());
|
||||
assertEquals("hello", allTypes.getOptionalString());
|
||||
}
|
||||
|
||||
/** Helper method for {@link #testParsingMergeLite()}.*/
|
||||
private void assertMessageMerged(TestAllTypesLite allTypes)
|
||||
throws Exception {
|
||||
assertEquals(3, allTypes.getOptionalInt32());
|
||||
assertEquals(2, allTypes.getOptionalInt64());
|
||||
assertEquals("hello", allTypes.getOptionalString());
|
||||
}
|
||||
|
||||
public void testParsingMerge() throws Exception {
|
||||
// Build messages.
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestAllTypes msg1 = builder.setOptionalInt32(1).build();
|
||||
builder.clear();
|
||||
TestAllTypes msg2 = builder.setOptionalInt64(2).build();
|
||||
builder.clear();
|
||||
TestAllTypes msg3 = builder.setOptionalInt32(3)
|
||||
.setOptionalString("hello").build();
|
||||
|
||||
// Build groups.
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG1 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG2 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG3 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG1 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG2 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG3 =
|
||||
TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
|
||||
// Assign and serialize RepeatedFieldsGenerator.
|
||||
ByteString data = TestParsingMerge.RepeatedFieldsGenerator.newBuilder()
|
||||
.addField1(msg1).addField1(msg2).addField1(msg3)
|
||||
.addField2(msg1).addField2(msg2).addField2(msg3)
|
||||
.addField3(msg1).addField3(msg2).addField3(msg3)
|
||||
.addGroup1(optionalG1).addGroup1(optionalG2).addGroup1(optionalG3)
|
||||
.addGroup2(repeatedG1).addGroup2(repeatedG2).addGroup2(repeatedG3)
|
||||
.addExt1(msg1).addExt1(msg2).addExt1(msg3)
|
||||
.addExt2(msg1).addExt2(msg2).addExt2(msg3)
|
||||
.build().toByteString();
|
||||
|
||||
// Parse TestParsingMerge.
|
||||
ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
UnittestProto.registerAllExtensions(registry);
|
||||
TestParsingMerge parsingMerge =
|
||||
TestParsingMerge.PARSER.parseFrom(data, registry);
|
||||
|
||||
// Required and optional fields should be merged.
|
||||
assertMessageMerged(parsingMerge.getRequiredAllTypes());
|
||||
assertMessageMerged(parsingMerge.getOptionalAllTypes());
|
||||
assertMessageMerged(
|
||||
parsingMerge.getOptionalGroup().getOptionalGroupAllTypes());
|
||||
assertMessageMerged(parsingMerge.getExtension(
|
||||
TestParsingMerge.optionalExt));
|
||||
|
||||
// Repeated fields should not be merged.
|
||||
assertEquals(3, parsingMerge.getRepeatedAllTypesCount());
|
||||
assertEquals(3, parsingMerge.getRepeatedGroupCount());
|
||||
assertEquals(3, parsingMerge.getExtensionCount(
|
||||
TestParsingMerge.repeatedExt));
|
||||
}
|
||||
|
||||
public void testParsingMergeLite() throws Exception {
|
||||
// Build messages.
|
||||
TestAllTypesLite.Builder builder =
|
||||
TestAllTypesLite.newBuilder();
|
||||
TestAllTypesLite msg1 = builder.setOptionalInt32(1).build();
|
||||
builder.clear();
|
||||
TestAllTypesLite msg2 = builder.setOptionalInt64(2).build();
|
||||
builder.clear();
|
||||
TestAllTypesLite msg3 = builder.setOptionalInt32(3)
|
||||
.setOptionalString("hello").build();
|
||||
|
||||
// Build groups.
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG1 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG2 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG3 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG1 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg1).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG2 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg2).build();
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG3 =
|
||||
TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
|
||||
.setField1(msg3).build();
|
||||
|
||||
// Assign and serialize RepeatedFieldsGenerator.
|
||||
ByteString data = TestParsingMergeLite.RepeatedFieldsGenerator.newBuilder()
|
||||
.addField1(msg1).addField1(msg2).addField1(msg3)
|
||||
.addField2(msg1).addField2(msg2).addField2(msg3)
|
||||
.addField3(msg1).addField3(msg2).addField3(msg3)
|
||||
.addGroup1(optionalG1).addGroup1(optionalG2).addGroup1(optionalG3)
|
||||
.addGroup2(repeatedG1).addGroup2(repeatedG2).addGroup2(repeatedG3)
|
||||
.addExt1(msg1).addExt1(msg2).addExt1(msg3)
|
||||
.addExt2(msg1).addExt2(msg2).addExt2(msg3)
|
||||
.build().toByteString();
|
||||
|
||||
// Parse TestParsingMergeLite.
|
||||
ExtensionRegistry registry = ExtensionRegistry.newInstance();
|
||||
UnittestLite.registerAllExtensions(registry);
|
||||
TestParsingMergeLite parsingMerge =
|
||||
TestParsingMergeLite.PARSER.parseFrom(data, registry);
|
||||
|
||||
// Required and optional fields should be merged.
|
||||
assertMessageMerged(parsingMerge.getRequiredAllTypes());
|
||||
assertMessageMerged(parsingMerge.getOptionalAllTypes());
|
||||
assertMessageMerged(
|
||||
parsingMerge.getOptionalGroup().getOptionalGroupAllTypes());
|
||||
assertMessageMerged(parsingMerge.getExtension(
|
||||
TestParsingMergeLite.optionalExt));
|
||||
|
||||
// Repeated fields should not be merged.
|
||||
assertEquals(3, parsingMerge.getRepeatedAllTypesCount());
|
||||
assertEquals(3, parsingMerge.getRepeatedGroupCount());
|
||||
assertEquals(3, parsingMerge.getExtensionCount(
|
||||
TestParsingMergeLite.repeatedExt));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for {@link RepeatedFieldBuilder}. This tests basic functionality.
|
||||
* More extensive testing is provided via other tests that exercise the
|
||||
* builder.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class RepeatedFieldBuilderTest extends TestCase {
|
||||
|
||||
public void testBasicUse() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
|
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32());
|
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32());
|
||||
|
||||
List<TestAllTypes> list = builder.build();
|
||||
assertEquals(2, list.size());
|
||||
assertEquals(0, list.get(0).getOptionalInt32());
|
||||
assertEquals(1, list.get(1).getOptionalInt32());
|
||||
assertIsUnmodifiable(list);
|
||||
|
||||
// Make sure it doesn't change.
|
||||
List<TestAllTypes> list2 = builder.build();
|
||||
assertSame(list, list2);
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
}
|
||||
|
||||
public void testGoingBackAndForth() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
|
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32());
|
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32());
|
||||
|
||||
// Convert to list
|
||||
List<TestAllTypes> list = builder.build();
|
||||
assertEquals(2, list.size());
|
||||
assertEquals(0, list.get(0).getOptionalInt32());
|
||||
assertEquals(1, list.get(1).getOptionalInt32());
|
||||
assertIsUnmodifiable(list);
|
||||
|
||||
// Update 0th item
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
builder.getBuilder(0).setOptionalString("foo");
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
list = builder.build();
|
||||
assertEquals(2, list.size());
|
||||
assertEquals(0, list.get(0).getOptionalInt32());
|
||||
assertEquals("foo", list.get(0).getOptionalString());
|
||||
assertEquals(1, list.get(1).getOptionalInt32());
|
||||
assertIsUnmodifiable(list);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
}
|
||||
|
||||
public void testVariousMethods() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build());
|
||||
builder.addBuilder(0, TestAllTypes.getDefaultInstance())
|
||||
.setOptionalInt32(0);
|
||||
builder.addBuilder(TestAllTypes.getDefaultInstance()).setOptionalInt32(3);
|
||||
|
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32());
|
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32());
|
||||
assertEquals(2, builder.getMessage(2).getOptionalInt32());
|
||||
assertEquals(3, builder.getMessage(3).getOptionalInt32());
|
||||
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
List<TestAllTypes> messages = builder.build();
|
||||
assertEquals(4, messages.size());
|
||||
assertSame(messages, builder.build()); // expect same list
|
||||
|
||||
// Remove a message.
|
||||
builder.remove(2);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
assertEquals(3, builder.getCount());
|
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32());
|
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32());
|
||||
assertEquals(3, builder.getMessage(2).getOptionalInt32());
|
||||
|
||||
// Remove a builder.
|
||||
builder.remove(0);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
assertEquals(2, builder.getCount());
|
||||
assertEquals(1, builder.getMessage(0).getOptionalInt32());
|
||||
assertEquals(3, builder.getMessage(1).getOptionalInt32());
|
||||
|
||||
// Test clear.
|
||||
builder.clear();
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
assertEquals(0, builder.getCount());
|
||||
assertTrue(builder.isEmpty());
|
||||
}
|
||||
|
||||
public void testLists() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
|
||||
builder.addMessage(0,
|
||||
TestAllTypes.newBuilder().setOptionalInt32(0).build());
|
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32());
|
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32());
|
||||
|
||||
// Use list of builders.
|
||||
List<TestAllTypes.Builder> builders = builder.getBuilderList();
|
||||
assertEquals(0, builders.get(0).getOptionalInt32());
|
||||
assertEquals(1, builders.get(1).getOptionalInt32());
|
||||
builders.get(0).setOptionalInt32(10);
|
||||
builders.get(1).setOptionalInt32(11);
|
||||
|
||||
// Use list of protos
|
||||
List<TestAllTypes> protos = builder.getMessageList();
|
||||
assertEquals(10, protos.get(0).getOptionalInt32());
|
||||
assertEquals(11, protos.get(1).getOptionalInt32());
|
||||
|
||||
// Add an item to the builders and verify it's updated in both
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(12).build());
|
||||
assertEquals(3, builders.size());
|
||||
assertEquals(3, protos.size());
|
||||
}
|
||||
|
||||
private void assertIsUnmodifiable(List<?> list) {
|
||||
if (list == Collections.emptyList()) {
|
||||
// OKAY -- Need to check this b/c EmptyList allows you to call clear.
|
||||
} else {
|
||||
try {
|
||||
list.clear();
|
||||
fail("List wasn't immutable");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>
|
||||
newRepeatedFieldBuilder(GeneratedMessage.BuilderParent parent) {
|
||||
return new RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false,
|
||||
parent, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class tests {@link RopeByteString#substring(int, int)} by inheriting the tests from
|
||||
* {@link LiteralByteStringTest}. Only a couple of methods are overridden.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class RopeByteStringSubstringTest extends LiteralByteStringTest {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "RopeByteString";
|
||||
byte[] sourceBytes = ByteStringTest.getTestBytes(22341, 22337766L);
|
||||
Iterator<ByteString> iter = ByteStringTest.makeConcretePieces(sourceBytes).iterator();
|
||||
ByteString sourceString = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
sourceString = sourceString.concat(iter.next());
|
||||
}
|
||||
|
||||
int from = 1130;
|
||||
int to = sourceBytes.length - 5555;
|
||||
stringUnderTest = sourceString.substring(from, to);
|
||||
referenceBytes = new byte[to - from];
|
||||
System.arraycopy(sourceBytes, from, referenceBytes, 0, to - from);
|
||||
expectedHashCode = -1259260680;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(classUnderTest + " must have the expected tree depth",
|
||||
3, stringUnderTest.getTreeDepth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String sourceString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
|
||||
int copies = 250;
|
||||
|
||||
// By building the RopeByteString by concatenating, this is actually a fairly strenuous test.
|
||||
StringBuilder builder = new StringBuilder(copies * sourceString.length());
|
||||
ByteString unicode = ByteString.EMPTY;
|
||||
for (int i = 0; i < copies; ++i) {
|
||||
builder.append(sourceString);
|
||||
unicode = RopeByteString.concatenate(unicode, sourceByteString);
|
||||
}
|
||||
String testString = builder.toString();
|
||||
|
||||
// Do the substring part
|
||||
testString = testString.substring(2, testString.length() - 6);
|
||||
unicode = unicode.substring(2, unicode.size() - 6);
|
||||
|
||||
assertEquals(classUnderTest + " from string must have the expected type",
|
||||
classUnderTest, getActualClassName(unicode));
|
||||
String roundTripString = unicode.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode bytes must match",
|
||||
testString, roundTripString);
|
||||
ByteString flatString = ByteString.copyFromUtf8(testString);
|
||||
assertEquals(classUnderTest + " string must equal the flat string", flatString, unicode);
|
||||
assertEquals(classUnderTest + " string must must have same hashCode as the flat string",
|
||||
flatString.hashCode(), unicode.hashCode());
|
||||
}
|
||||
}
|
||||
115
java/src/test/java/com/google/protobuf/RopeByteStringTest.java
Normal file
115
java/src/test/java/com/google/protobuf/RopeByteStringTest.java
Normal file
@@ -0,0 +1,115 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This class tests {@link RopeByteString} by inheriting the tests from
|
||||
* {@link LiteralByteStringTest}. Only a couple of methods are overridden.
|
||||
*
|
||||
* <p>A full test of the result of {@link RopeByteString#substring(int, int)} is found in the
|
||||
* separate class {@link RopeByteStringSubstringTest}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
public class RopeByteStringTest extends LiteralByteStringTest {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
classUnderTest = "RopeByteString";
|
||||
referenceBytes = ByteStringTest.getTestBytes(22341, 22337766L);
|
||||
Iterator<ByteString> iter = ByteStringTest.makeConcretePieces(referenceBytes).iterator();
|
||||
stringUnderTest = iter.next();
|
||||
while (iter.hasNext()) {
|
||||
stringUnderTest = stringUnderTest.concat(iter.next());
|
||||
}
|
||||
expectedHashCode = -1214197238;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(classUnderTest + " must have the expected tree depth",
|
||||
4, stringUnderTest.getTreeDepth());
|
||||
}
|
||||
|
||||
public void testBalance() {
|
||||
int numberOfPieces = 10000;
|
||||
int pieceSize = 64;
|
||||
byte[] testBytes = ByteStringTest.getTestBytes(numberOfPieces * pieceSize, 113377L);
|
||||
|
||||
// Build up a big ByteString from smaller pieces to force a rebalance
|
||||
ByteString concatenated = ByteString.EMPTY;
|
||||
for (int i = 0; i < numberOfPieces; ++i) {
|
||||
concatenated = concatenated.concat(ByteString.copyFrom(testBytes, i * pieceSize, pieceSize));
|
||||
}
|
||||
|
||||
assertEquals(classUnderTest + " from string must have the expected type",
|
||||
classUnderTest, getActualClassName(concatenated));
|
||||
assertTrue(classUnderTest + " underlying bytes must match after balancing",
|
||||
Arrays.equals(testBytes, concatenated.toByteArray()));
|
||||
ByteString testString = ByteString.copyFrom(testBytes);
|
||||
assertTrue(classUnderTest + " balanced string must equal flat string",
|
||||
concatenated.equals(testString));
|
||||
assertTrue(classUnderTest + " flat string must equal balanced string",
|
||||
testString.equals(concatenated));
|
||||
assertEquals(classUnderTest + " balanced string must have same hash code as flat string",
|
||||
testString.hashCode(), concatenated.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testToString() throws UnsupportedEncodingException {
|
||||
String sourceString = "I love unicode \u1234\u5678 characters";
|
||||
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
|
||||
int copies = 250;
|
||||
|
||||
// By building the RopeByteString by concatenating, this is actually a fairly strenuous test.
|
||||
StringBuilder builder = new StringBuilder(copies * sourceString.length());
|
||||
ByteString unicode = ByteString.EMPTY;
|
||||
for (int i = 0; i < copies; ++i) {
|
||||
builder.append(sourceString);
|
||||
unicode = RopeByteString.concatenate(unicode, sourceByteString);
|
||||
}
|
||||
String testString = builder.toString();
|
||||
|
||||
assertEquals(classUnderTest + " from string must have the expected type",
|
||||
classUnderTest, getActualClassName(unicode));
|
||||
String roundTripString = unicode.toString(UTF_8);
|
||||
assertEquals(classUnderTest + " unicode bytes must match",
|
||||
testString, roundTripString);
|
||||
ByteString flatString = ByteString.copyFromUtf8(testString);
|
||||
assertEquals(classUnderTest + " string must equal the flat string", flatString, unicode);
|
||||
assertEquals(classUnderTest + " string must must have same hashCode as the flat string",
|
||||
flatString.hashCode(), unicode.hashCode());
|
||||
}
|
||||
}
|
||||
320
java/src/test/java/com/google/protobuf/ServiceTest.java
Normal file
320
java/src/test/java/com/google/protobuf/ServiceTest.java
Normal file
@@ -0,0 +1,320 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FileDescriptor;
|
||||
import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
import google.protobuf.no_generic_services_test.UnittestNoGenericServices;
|
||||
import protobuf_unittest.MessageWithNoOuter;
|
||||
import protobuf_unittest.ServiceWithNoOuter;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
import protobuf_unittest.UnittestProto.FooRequest;
|
||||
import protobuf_unittest.UnittestProto.FooResponse;
|
||||
import protobuf_unittest.UnittestProto.BarRequest;
|
||||
import protobuf_unittest.UnittestProto.BarResponse;
|
||||
|
||||
import org.easymock.classextension.EasyMock;
|
||||
import org.easymock.classextension.IMocksControl;
|
||||
import org.easymock.IArgumentMatcher;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests services and stubs.
|
||||
*
|
||||
* @author kenton@google.com Kenton Varda
|
||||
*/
|
||||
public class ServiceTest extends TestCase {
|
||||
private IMocksControl control;
|
||||
private RpcController mockController;
|
||||
|
||||
private final Descriptors.MethodDescriptor fooDescriptor =
|
||||
TestService.getDescriptor().getMethods().get(0);
|
||||
private final Descriptors.MethodDescriptor barDescriptor =
|
||||
TestService.getDescriptor().getMethods().get(1);
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
control = EasyMock.createStrictControl();
|
||||
mockController = control.createMock(RpcController.class);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/** Tests Service.callMethod(). */
|
||||
public void testCallMethod() throws Exception {
|
||||
FooRequest fooRequest = FooRequest.newBuilder().build();
|
||||
BarRequest barRequest = BarRequest.newBuilder().build();
|
||||
MockCallback<Message> fooCallback = new MockCallback<Message>();
|
||||
MockCallback<Message> barCallback = new MockCallback<Message>();
|
||||
TestService mockService = control.createMock(TestService.class);
|
||||
|
||||
mockService.foo(EasyMock.same(mockController), EasyMock.same(fooRequest),
|
||||
this.<FooResponse>wrapsCallback(fooCallback));
|
||||
mockService.bar(EasyMock.same(mockController), EasyMock.same(barRequest),
|
||||
this.<BarResponse>wrapsCallback(barCallback));
|
||||
control.replay();
|
||||
|
||||
mockService.callMethod(fooDescriptor, mockController,
|
||||
fooRequest, fooCallback);
|
||||
mockService.callMethod(barDescriptor, mockController,
|
||||
barRequest, barCallback);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/** Tests Service.get{Request,Response}Prototype(). */
|
||||
public void testGetPrototype() throws Exception {
|
||||
TestService mockService = control.createMock(TestService.class);
|
||||
|
||||
assertSame(mockService.getRequestPrototype(fooDescriptor),
|
||||
FooRequest.getDefaultInstance());
|
||||
assertSame(mockService.getResponsePrototype(fooDescriptor),
|
||||
FooResponse.getDefaultInstance());
|
||||
assertSame(mockService.getRequestPrototype(barDescriptor),
|
||||
BarRequest.getDefaultInstance());
|
||||
assertSame(mockService.getResponsePrototype(barDescriptor),
|
||||
BarResponse.getDefaultInstance());
|
||||
}
|
||||
|
||||
/** Tests generated stubs. */
|
||||
public void testStub() throws Exception {
|
||||
FooRequest fooRequest = FooRequest.newBuilder().build();
|
||||
BarRequest barRequest = BarRequest.newBuilder().build();
|
||||
MockCallback<FooResponse> fooCallback = new MockCallback<FooResponse>();
|
||||
MockCallback<BarResponse> barCallback = new MockCallback<BarResponse>();
|
||||
RpcChannel mockChannel = control.createMock(RpcChannel.class);
|
||||
TestService stub = TestService.newStub(mockChannel);
|
||||
|
||||
mockChannel.callMethod(
|
||||
EasyMock.same(fooDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(fooRequest),
|
||||
EasyMock.same(FooResponse.getDefaultInstance()),
|
||||
this.<Message>wrapsCallback(fooCallback));
|
||||
mockChannel.callMethod(
|
||||
EasyMock.same(barDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(barRequest),
|
||||
EasyMock.same(BarResponse.getDefaultInstance()),
|
||||
this.<Message>wrapsCallback(barCallback));
|
||||
control.replay();
|
||||
|
||||
stub.foo(mockController, fooRequest, fooCallback);
|
||||
stub.bar(mockController, barRequest, barCallback);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
/** Tests generated blocking stubs. */
|
||||
public void testBlockingStub() throws Exception {
|
||||
FooRequest fooRequest = FooRequest.newBuilder().build();
|
||||
BarRequest barRequest = BarRequest.newBuilder().build();
|
||||
BlockingRpcChannel mockChannel =
|
||||
control.createMock(BlockingRpcChannel.class);
|
||||
TestService.BlockingInterface stub =
|
||||
TestService.newBlockingStub(mockChannel);
|
||||
|
||||
FooResponse fooResponse = FooResponse.newBuilder().build();
|
||||
BarResponse barResponse = BarResponse.newBuilder().build();
|
||||
|
||||
EasyMock.expect(mockChannel.callBlockingMethod(
|
||||
EasyMock.same(fooDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(fooRequest),
|
||||
EasyMock.same(FooResponse.getDefaultInstance()))).andReturn(fooResponse);
|
||||
EasyMock.expect(mockChannel.callBlockingMethod(
|
||||
EasyMock.same(barDescriptor),
|
||||
EasyMock.same(mockController),
|
||||
EasyMock.same(barRequest),
|
||||
EasyMock.same(BarResponse.getDefaultInstance()))).andReturn(barResponse);
|
||||
control.replay();
|
||||
|
||||
assertSame(fooResponse, stub.foo(mockController, fooRequest));
|
||||
assertSame(barResponse, stub.bar(mockController, barRequest));
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testNewReflectiveService() {
|
||||
ServiceWithNoOuter.Interface impl =
|
||||
control.createMock(ServiceWithNoOuter.Interface.class);
|
||||
RpcController controller = control.createMock(RpcController.class);
|
||||
Service service = ServiceWithNoOuter.newReflectiveService(impl);
|
||||
|
||||
MethodDescriptor fooMethod =
|
||||
ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
|
||||
MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
|
||||
RpcCallback<Message> callback = new RpcCallback<Message>() {
|
||||
public void run(Message parameter) {
|
||||
// No reason this should be run.
|
||||
fail();
|
||||
}
|
||||
};
|
||||
RpcCallback<TestAllTypes> specializedCallback =
|
||||
RpcUtil.specializeCallback(callback);
|
||||
|
||||
impl.foo(EasyMock.same(controller), EasyMock.same(request),
|
||||
EasyMock.same(specializedCallback));
|
||||
EasyMock.expectLastCall();
|
||||
|
||||
control.replay();
|
||||
|
||||
service.callMethod(fooMethod, controller, request, callback);
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testNewReflectiveBlockingService() throws ServiceException {
|
||||
ServiceWithNoOuter.BlockingInterface impl =
|
||||
control.createMock(ServiceWithNoOuter.BlockingInterface.class);
|
||||
RpcController controller = control.createMock(RpcController.class);
|
||||
BlockingService service =
|
||||
ServiceWithNoOuter.newReflectiveBlockingService(impl);
|
||||
|
||||
MethodDescriptor fooMethod =
|
||||
ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
|
||||
MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
|
||||
|
||||
TestAllTypes expectedResponse = TestAllTypes.getDefaultInstance();
|
||||
EasyMock.expect(impl.foo(EasyMock.same(controller), EasyMock.same(request)))
|
||||
.andReturn(expectedResponse);
|
||||
|
||||
control.replay();
|
||||
|
||||
Message response =
|
||||
service.callBlockingMethod(fooMethod, controller, request);
|
||||
assertEquals(expectedResponse, response);
|
||||
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testNoGenericServices() throws Exception {
|
||||
// Non-services should be usable.
|
||||
UnittestNoGenericServices.TestMessage message =
|
||||
UnittestNoGenericServices.TestMessage.newBuilder()
|
||||
.setA(123)
|
||||
.setExtension(UnittestNoGenericServices.testExtension, 456)
|
||||
.build();
|
||||
assertEquals(123, message.getA());
|
||||
assertEquals(1, UnittestNoGenericServices.TestEnum.FOO.getNumber());
|
||||
|
||||
// Build a list of the class names nested in UnittestNoGenericServices.
|
||||
String outerName = "google.protobuf.no_generic_services_test." +
|
||||
"UnittestNoGenericServices";
|
||||
Class<?> outerClass = Class.forName(outerName);
|
||||
|
||||
Set<String> innerClassNames = new HashSet<String>();
|
||||
for (Class<?> innerClass : outerClass.getClasses()) {
|
||||
String fullName = innerClass.getName();
|
||||
// Figure out the unqualified name of the inner class.
|
||||
// Note: Surprisingly, the full name of an inner class will be separated
|
||||
// from the outer class name by a '$' rather than a '.'. This is not
|
||||
// mentioned in the documentation for java.lang.Class. I don't want to
|
||||
// make assumptions, so I'm just going to accept any character as the
|
||||
// separator.
|
||||
assertTrue(fullName.startsWith(outerName));
|
||||
|
||||
if (!Service.class.isAssignableFrom(innerClass) &&
|
||||
!Message.class.isAssignableFrom(innerClass) &&
|
||||
!ProtocolMessageEnum.class.isAssignableFrom(innerClass)) {
|
||||
// Ignore any classes not generated by the base code generator.
|
||||
continue;
|
||||
}
|
||||
|
||||
innerClassNames.add(fullName.substring(outerName.length() + 1));
|
||||
}
|
||||
|
||||
// No service class should have been generated.
|
||||
assertTrue(innerClassNames.contains("TestMessage"));
|
||||
assertTrue(innerClassNames.contains("TestEnum"));
|
||||
assertFalse(innerClassNames.contains("TestService"));
|
||||
|
||||
// But descriptors are there.
|
||||
FileDescriptor file = UnittestNoGenericServices.getDescriptor();
|
||||
assertEquals(1, file.getServices().size());
|
||||
assertEquals("TestService", file.getServices().get(0).getName());
|
||||
assertEquals(1, file.getServices().get(0).getMethods().size());
|
||||
assertEquals("Foo",
|
||||
file.getServices().get(0).getMethods().get(0).getName());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* wrapsCallback() is an EasyMock argument predicate. wrapsCallback(c)
|
||||
* matches a callback if calling that callback causes c to be called.
|
||||
* In other words, c wraps the given callback.
|
||||
*/
|
||||
private <Type extends Message> RpcCallback<Type> wrapsCallback(
|
||||
MockCallback<?> callback) {
|
||||
EasyMock.reportMatcher(new WrapsCallback(callback));
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The parameter to wrapsCallback() must be a MockCallback. */
|
||||
private static class MockCallback<Type extends Message>
|
||||
implements RpcCallback<Type> {
|
||||
private boolean called = false;
|
||||
|
||||
public boolean isCalled() { return called; }
|
||||
|
||||
public void reset() { called = false; }
|
||||
public void run(Type message) { called = true; }
|
||||
}
|
||||
|
||||
/** Implementation of the wrapsCallback() argument matcher. */
|
||||
private static class WrapsCallback implements IArgumentMatcher {
|
||||
private MockCallback<?> callback;
|
||||
|
||||
public WrapsCallback(MockCallback<?> callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean matches(Object actual) {
|
||||
if (!(actual instanceof RpcCallback)) {
|
||||
return false;
|
||||
}
|
||||
RpcCallback actualCallback = (RpcCallback)actual;
|
||||
|
||||
callback.reset();
|
||||
actualCallback.run(null);
|
||||
return callback.isCalled();
|
||||
}
|
||||
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("wrapsCallback(mockCallback)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link SingleFieldBuilder}. This tests basic functionality.
|
||||
* More extensive testing is provided via other tests that exercise the
|
||||
* builder.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class SingleFieldBuilderTest extends TestCase {
|
||||
|
||||
public void testBasicUseAndInvalidations() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>(
|
||||
TestAllTypes.getDefaultInstance(),
|
||||
mockParent,
|
||||
false);
|
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
|
||||
assertEquals(TestAllTypes.getDefaultInstance(),
|
||||
builder.getBuilder().buildPartial());
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
|
||||
builder.getBuilder().setOptionalInt32(10);
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
TestAllTypes message = builder.build();
|
||||
assertEquals(10, message.getOptionalInt32());
|
||||
|
||||
// Test that we receive invalidations now that build has been called.
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
builder.getBuilder().setOptionalInt32(20);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
|
||||
// Test that we don't keep getting invalidations on every change
|
||||
builder.getBuilder().setOptionalInt32(30);
|
||||
assertEquals(1, mockParent.getInvalidationCount());
|
||||
|
||||
}
|
||||
|
||||
public void testSetMessage() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>(
|
||||
TestAllTypes.getDefaultInstance(),
|
||||
mockParent,
|
||||
false);
|
||||
builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
|
||||
assertEquals(0, builder.getMessage().getOptionalInt32());
|
||||
|
||||
// Update message using the builder
|
||||
builder.getBuilder().setOptionalInt32(1);
|
||||
assertEquals(0, mockParent.getInvalidationCount());
|
||||
assertEquals(1, builder.getBuilder().getOptionalInt32());
|
||||
assertEquals(1, builder.getMessage().getOptionalInt32());
|
||||
builder.build();
|
||||
builder.getBuilder().setOptionalInt32(2);
|
||||
assertEquals(2, builder.getBuilder().getOptionalInt32());
|
||||
assertEquals(2, builder.getMessage().getOptionalInt32());
|
||||
|
||||
// Make sure message stays cached
|
||||
assertSame(builder.getMessage(), builder.getMessage());
|
||||
}
|
||||
|
||||
public void testClear() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>(
|
||||
TestAllTypes.getDefaultInstance(),
|
||||
mockParent,
|
||||
false);
|
||||
builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
|
||||
assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
|
||||
builder.clear();
|
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
|
||||
|
||||
builder.getBuilder().setOptionalInt32(1);
|
||||
assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
|
||||
builder.clear();
|
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
|
||||
}
|
||||
|
||||
public void testMerge() {
|
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
|
||||
SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder> builder =
|
||||
new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
|
||||
TestAllTypesOrBuilder>(
|
||||
TestAllTypes.getDefaultInstance(),
|
||||
mockParent,
|
||||
false);
|
||||
|
||||
// Merge into default field.
|
||||
builder.mergeFrom(TestAllTypes.getDefaultInstance());
|
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage());
|
||||
|
||||
// Merge into non-default field on existing builder.
|
||||
builder.getBuilder().setOptionalInt32(2);
|
||||
builder.mergeFrom(TestAllTypes.newBuilder()
|
||||
.setOptionalDouble(4.0)
|
||||
.buildPartial());
|
||||
assertEquals(2, builder.getMessage().getOptionalInt32());
|
||||
assertEquals(4.0, builder.getMessage().getOptionalDouble());
|
||||
|
||||
// Merge into non-default field on existing message
|
||||
builder.setMessage(TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(10)
|
||||
.buildPartial());
|
||||
builder.mergeFrom(TestAllTypes.newBuilder()
|
||||
.setOptionalDouble(5.0)
|
||||
.buildPartial());
|
||||
assertEquals(10, builder.getMessage().getOptionalInt32());
|
||||
assertEquals(5.0, builder.getMessage().getOptionalDouble());
|
||||
}
|
||||
}
|
||||
420
java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
Normal file
420
java/src/test/java/com/google/protobuf/SmallSortedMapTest.java
Normal file
@@ -0,0 +1,420 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* @author darick@google.com Darick Tong
|
||||
*/
|
||||
public class SmallSortedMapTest extends TestCase {
|
||||
// java.util.AbstractMap.SimpleEntry is private in JDK 1.5. We re-implement it
|
||||
// here for JDK 1.5 users.
|
||||
private static class SimpleEntry<K, V> implements Map.Entry<K, V> {
|
||||
private final K key;
|
||||
private V value;
|
||||
|
||||
SimpleEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public V setValue(V value) {
|
||||
V oldValue = this.value;
|
||||
this.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
private static boolean eq(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
Map.Entry e = (Map.Entry) o;
|
||||
return eq(key, e.getKey()) && eq(value, e.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ((key == null) ? 0 : key.hashCode()) ^
|
||||
((value == null) ? 0 : value.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
public void testPutAndGetArrayEntriesOnly() {
|
||||
runPutAndGetTest(3);
|
||||
}
|
||||
|
||||
public void testPutAndGetOverflowEntries() {
|
||||
runPutAndGetTest(6);
|
||||
}
|
||||
|
||||
private void runPutAndGetTest(int numElements) {
|
||||
// Test with even and odd arraySize
|
||||
SmallSortedMap<Integer, Integer> map1 =
|
||||
SmallSortedMap.newInstanceForTest(3);
|
||||
SmallSortedMap<Integer, Integer> map2 =
|
||||
SmallSortedMap.newInstanceForTest(4);
|
||||
SmallSortedMap<Integer, Integer> map3 =
|
||||
SmallSortedMap.newInstanceForTest(3);
|
||||
SmallSortedMap<Integer, Integer> map4 =
|
||||
SmallSortedMap.newInstanceForTest(4);
|
||||
|
||||
// Test with puts in ascending order.
|
||||
for (int i = 0; i < numElements; i++) {
|
||||
assertNull(map1.put(i, i + 1));
|
||||
assertNull(map2.put(i, i + 1));
|
||||
}
|
||||
// Test with puts in descending order.
|
||||
for (int i = numElements - 1; i >= 0; i--) {
|
||||
assertNull(map3.put(i, i + 1));
|
||||
assertNull(map4.put(i, i + 1));
|
||||
}
|
||||
|
||||
assertEquals(Math.min(3, numElements), map1.getNumArrayEntries());
|
||||
assertEquals(Math.min(4, numElements), map2.getNumArrayEntries());
|
||||
assertEquals(Math.min(3, numElements), map3.getNumArrayEntries());
|
||||
assertEquals(Math.min(4, numElements), map4.getNumArrayEntries());
|
||||
|
||||
List<SmallSortedMap<Integer, Integer>> allMaps =
|
||||
new ArrayList<SmallSortedMap<Integer, Integer>>();
|
||||
allMaps.add(map1);
|
||||
allMaps.add(map2);
|
||||
allMaps.add(map3);
|
||||
allMaps.add(map4);
|
||||
|
||||
for (SmallSortedMap<Integer, Integer> map : allMaps) {
|
||||
assertEquals(numElements, map.size());
|
||||
for (int i = 0; i < numElements; i++) {
|
||||
assertEquals(new Integer(i + 1), map.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(map1, map2);
|
||||
assertEquals(map2, map3);
|
||||
assertEquals(map3, map4);
|
||||
}
|
||||
|
||||
public void testReplacingPut() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
assertNull(map.remove(i + 1));
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(new Integer(i + 1), map.put(i, i + 2));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemove() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
assertNull(map.remove(i + 1));
|
||||
}
|
||||
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(3, map.getNumOverflowEntries());
|
||||
assertEquals(6, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 1, 2, 3, 4, 5), map.keySet());
|
||||
|
||||
assertEquals(new Integer(2), map.remove(1));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(2, map.getNumOverflowEntries());
|
||||
assertEquals(5, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 2, 3, 4, 5), map.keySet());
|
||||
|
||||
assertEquals(new Integer(5), map.remove(4));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(1, map.getNumOverflowEntries());
|
||||
assertEquals(4, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 2, 3, 5), map.keySet());
|
||||
|
||||
assertEquals(new Integer(4), map.remove(3));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(3, map.size());
|
||||
assertEquals(makeSortedKeySet(0, 2, 5), map.keySet());
|
||||
|
||||
assertNull(map.remove(3));
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(3, map.size());
|
||||
|
||||
assertEquals(new Integer(1), map.remove(0));
|
||||
assertEquals(2, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(2, map.size());
|
||||
}
|
||||
|
||||
public void testClear() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
map.clear();
|
||||
assertEquals(0, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testGetArrayEntryAndOverflowEntries() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Map.Entry<Integer, Integer> entry = map.getArrayEntryAt(i);
|
||||
assertEquals(new Integer(i), entry.getKey());
|
||||
assertEquals(new Integer(i + 1), entry.getValue());
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it =
|
||||
map.getOverflowEntries().iterator();
|
||||
for (int i = 3; i < 6; i++) {
|
||||
assertTrue(it.hasNext());
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
assertEquals(new Integer(i), entry.getKey());
|
||||
assertEquals(new Integer(i + 1), entry.getValue());
|
||||
}
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
public void testEntrySetContains() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertTrue(
|
||||
entrySet.contains(new SimpleEntry<Integer, Integer>(i, i + 1)));
|
||||
assertFalse(
|
||||
entrySet.contains(new SimpleEntry<Integer, Integer>(i, i)));
|
||||
}
|
||||
}
|
||||
|
||||
public void testEntrySetAdd() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Map.Entry<Integer, Integer> entry =
|
||||
new SimpleEntry<Integer, Integer>(i, i + 1);
|
||||
assertTrue(entrySet.add(entry));
|
||||
assertFalse(entrySet.add(entry));
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(new Integer(i + 1), map.get(i));
|
||||
}
|
||||
assertEquals(3, map.getNumArrayEntries());
|
||||
assertEquals(3, map.getNumOverflowEntries());
|
||||
assertEquals(6, map.size());
|
||||
}
|
||||
|
||||
public void testEntrySetRemove() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Map.Entry<Integer, Integer> entry =
|
||||
new SimpleEntry<Integer, Integer>(i, i + 1);
|
||||
assertTrue(entrySet.remove(entry));
|
||||
assertFalse(entrySet.remove(entry));
|
||||
}
|
||||
assertTrue(map.isEmpty());
|
||||
assertEquals(0, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testEntrySetClear() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
map.entrySet().clear();
|
||||
assertTrue(map.isEmpty());
|
||||
assertEquals(0, map.getNumArrayEntries());
|
||||
assertEquals(0, map.getNumOverflowEntries());
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
public void testEntrySetIteratorNext() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertTrue(it.hasNext());
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
assertEquals(new Integer(i), entry.getKey());
|
||||
assertEquals(new Integer(i + 1), entry.getValue());
|
||||
}
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
public void testEntrySetIteratorRemove() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertTrue(map.containsKey(i));
|
||||
it.next();
|
||||
it.remove();
|
||||
assertFalse(map.containsKey(i));
|
||||
assertEquals(6 - i - 1, map.size());
|
||||
}
|
||||
}
|
||||
|
||||
public void testMapEntryModification() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
entry.setValue(i + 23);
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(new Integer(i + 23), map.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public void testMakeImmutable() {
|
||||
SmallSortedMap<Integer, Integer> map = SmallSortedMap.newInstanceForTest(3);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertNull(map.put(i, i + 1));
|
||||
}
|
||||
map.makeImmutable();
|
||||
assertEquals(new Integer(1), map.get(0));
|
||||
assertEquals(6, map.size());
|
||||
|
||||
try {
|
||||
map.put(23, 23);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Map<Integer, Integer> other = new HashMap<Integer, Integer>();
|
||||
other.put(23, 23);
|
||||
try {
|
||||
map.putAll(other);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
map.remove(0);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
map.clear();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
|
||||
try {
|
||||
entrySet.clear();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Integer, Integer>> it = entrySet.iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<Integer, Integer> entry = it.next();
|
||||
try {
|
||||
entry.setValue(0);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
try {
|
||||
it.remove();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
Set<Integer> keySet = map.keySet();
|
||||
try {
|
||||
keySet.clear();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
|
||||
Iterator<Integer> keys = keySet.iterator();
|
||||
while (keys.hasNext()) {
|
||||
Integer key = keys.next();
|
||||
try {
|
||||
keySet.remove(key);
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
try {
|
||||
keys.remove();
|
||||
fail("Expected UnsupportedOperationException");
|
||||
} catch (UnsupportedOperationException expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Integer> makeSortedKeySet(Integer... keys) {
|
||||
return new TreeSet<Integer>(Arrays.<Integer>asList(keys));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests that proto2 api generation doesn't cause compile errors when
|
||||
* compiling protocol buffers that have names that would otherwise conflict
|
||||
* if not fully qualified (like @Deprecated and @Override).
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class TestBadIdentifiers extends TestCase {
|
||||
|
||||
public void testCompilation() {
|
||||
// If this compiles, it means the generation was correct.
|
||||
TestBadIdentifiersProto.Deprecated.newBuilder();
|
||||
TestBadIdentifiersProto.Override.newBuilder();
|
||||
}
|
||||
|
||||
public void testGetDescriptor() {
|
||||
Descriptors.FileDescriptor fileDescriptor =
|
||||
TestBadIdentifiersProto.getDescriptor();
|
||||
String descriptorField = TestBadIdentifiersProto.Descriptor
|
||||
.getDefaultInstance().getDescriptor();
|
||||
Descriptors.Descriptor protoDescriptor = TestBadIdentifiersProto.Descriptor
|
||||
.getDefaultInstance().getDescriptorForType();
|
||||
String nestedDescriptorField = TestBadIdentifiersProto.Descriptor
|
||||
.NestedDescriptor.getDefaultInstance().getDescriptor();
|
||||
Descriptors.Descriptor nestedProtoDescriptor = TestBadIdentifiersProto
|
||||
.Descriptor.NestedDescriptor.getDefaultInstance()
|
||||
.getDescriptorForType();
|
||||
}
|
||||
}
|
||||
3955
java/src/test/java/com/google/protobuf/TestUtil.java
Normal file
3955
java/src/test/java/com/google/protobuf/TestUtil.java
Normal file
File diff suppressed because it is too large
Load Diff
786
java/src/test/java/com/google/protobuf/TextFormatTest.java
Normal file
786
java/src/test/java/com/google/protobuf/TextFormatTest.java
Normal file
@@ -0,0 +1,786 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSet;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
|
||||
import protobuf_unittest.UnittestProto.OneString;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* Test case for {@link TextFormat}.
|
||||
*
|
||||
* TODO(wenboz): ExtensionTest and rest of text_format_unittest.cc.
|
||||
*
|
||||
* @author wenboz@google.com (Wenbo Zhu)
|
||||
*/
|
||||
public class TextFormatTest extends TestCase {
|
||||
|
||||
// A basic string with different escapable characters for testing.
|
||||
private final static String kEscapeTestString =
|
||||
"\"A string with ' characters \n and \r newlines and \t tabs and \001 "
|
||||
+ "slashes \\";
|
||||
|
||||
// A representation of the above string with all the characters escaped.
|
||||
private final static String kEscapeTestStringEscaped =
|
||||
"\\\"A string with \\' characters \\n and \\r newlines "
|
||||
+ "and \\t tabs and \\001 slashes \\\\";
|
||||
|
||||
private static String allFieldsSetText = TestUtil.readTextFromFile(
|
||||
"text_format_unittest_data.txt");
|
||||
private static String allExtensionsSetText = TestUtil.readTextFromFile(
|
||||
"text_format_unittest_extensions_data.txt");
|
||||
|
||||
private static String exoticText =
|
||||
"repeated_int32: -1\n" +
|
||||
"repeated_int32: -2147483648\n" +
|
||||
"repeated_int64: -1\n" +
|
||||
"repeated_int64: -9223372036854775808\n" +
|
||||
"repeated_uint32: 4294967295\n" +
|
||||
"repeated_uint32: 2147483648\n" +
|
||||
"repeated_uint64: 18446744073709551615\n" +
|
||||
"repeated_uint64: 9223372036854775808\n" +
|
||||
"repeated_double: 123.0\n" +
|
||||
"repeated_double: 123.5\n" +
|
||||
"repeated_double: 0.125\n" +
|
||||
"repeated_double: .125\n" +
|
||||
"repeated_double: -.125\n" +
|
||||
"repeated_double: 1.23E17\n" +
|
||||
"repeated_double: 1.23E+17\n" +
|
||||
"repeated_double: -1.23e-17\n" +
|
||||
"repeated_double: .23e+17\n" +
|
||||
"repeated_double: -.23E17\n" +
|
||||
"repeated_double: 1.235E22\n" +
|
||||
"repeated_double: 1.235E-18\n" +
|
||||
"repeated_double: 123.456789\n" +
|
||||
"repeated_double: Infinity\n" +
|
||||
"repeated_double: -Infinity\n" +
|
||||
"repeated_double: NaN\n" +
|
||||
"repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"" +
|
||||
"\\341\\210\\264\"\n" +
|
||||
"repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n";
|
||||
|
||||
private static String canonicalExoticText =
|
||||
exoticText.replace(": .", ": 0.").replace(": -.", ": -0.") // short-form double
|
||||
.replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16");
|
||||
|
||||
private String messageSetText =
|
||||
"[protobuf_unittest.TestMessageSetExtension1] {\n" +
|
||||
" i: 123\n" +
|
||||
"}\n" +
|
||||
"[protobuf_unittest.TestMessageSetExtension2] {\n" +
|
||||
" str: \"foo\"\n" +
|
||||
"}\n";
|
||||
|
||||
/** Print TestAllTypes and compare with golden file. */
|
||||
public void testPrintMessage() throws Exception {
|
||||
String javaText = TextFormat.printToString(TestUtil.getAllSet());
|
||||
|
||||
// Java likes to add a trailing ".0" to floats and doubles. C printf
|
||||
// (with %g format) does not. Our golden files are used for both
|
||||
// C++ and Java TextFormat classes, so we need to conform.
|
||||
javaText = javaText.replace(".0\n", "\n");
|
||||
|
||||
assertEquals(allFieldsSetText, javaText);
|
||||
}
|
||||
|
||||
/** Print TestAllTypes as Builder and compare with golden file. */
|
||||
public void testPrintMessageBuilder() throws Exception {
|
||||
String javaText = TextFormat.printToString(TestUtil.getAllSetBuilder());
|
||||
|
||||
// Java likes to add a trailing ".0" to floats and doubles. C printf
|
||||
// (with %g format) does not. Our golden files are used for both
|
||||
// C++ and Java TextFormat classes, so we need to conform.
|
||||
javaText = javaText.replace(".0\n", "\n");
|
||||
|
||||
assertEquals(allFieldsSetText, javaText);
|
||||
}
|
||||
|
||||
/** Print TestAllExtensions and compare with golden file. */
|
||||
public void testPrintExtensions() throws Exception {
|
||||
String javaText = TextFormat.printToString(TestUtil.getAllExtensionsSet());
|
||||
|
||||
// Java likes to add a trailing ".0" to floats and doubles. C printf
|
||||
// (with %g format) does not. Our golden files are used for both
|
||||
// C++ and Java TextFormat classes, so we need to conform.
|
||||
javaText = javaText.replace(".0\n", "\n");
|
||||
|
||||
assertEquals(allExtensionsSetText, javaText);
|
||||
}
|
||||
|
||||
// Creates an example unknown field set.
|
||||
private UnknownFieldSet makeUnknownFieldSet() {
|
||||
return UnknownFieldSet.newBuilder()
|
||||
.addField(5,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.addFixed32(2)
|
||||
.addFixed64(3)
|
||||
.addLengthDelimited(ByteString.copyFromUtf8("4"))
|
||||
.addGroup(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(10,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(5)
|
||||
.build())
|
||||
.build())
|
||||
.build())
|
||||
.addField(8,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.addVarint(2)
|
||||
.addVarint(3)
|
||||
.build())
|
||||
.addField(15,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(0xABCDEF1234567890L)
|
||||
.addFixed32(0xABCD1234)
|
||||
.addFixed64(0xABCDEF1234567890L)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void testPrintUnknownFields() throws Exception {
|
||||
// Test printing of unknown fields in a message.
|
||||
|
||||
TestEmptyMessage message =
|
||||
TestEmptyMessage.newBuilder()
|
||||
.setUnknownFields(makeUnknownFieldSet())
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"5: 1\n" +
|
||||
"5: 0x00000002\n" +
|
||||
"5: 0x0000000000000003\n" +
|
||||
"5: \"4\"\n" +
|
||||
"5 {\n" +
|
||||
" 10: 5\n" +
|
||||
"}\n" +
|
||||
"8: 1\n" +
|
||||
"8: 2\n" +
|
||||
"8: 3\n" +
|
||||
"15: 12379813812177893520\n" +
|
||||
"15: 0xabcd1234\n" +
|
||||
"15: 0xabcdef1234567890\n",
|
||||
TextFormat.printToString(message));
|
||||
}
|
||||
|
||||
public void testPrintField() throws Exception {
|
||||
final FieldDescriptor dataField =
|
||||
OneString.getDescriptor().findFieldByName("data");
|
||||
assertEquals(
|
||||
"data: \"test data\"\n",
|
||||
TextFormat.printFieldToString(dataField, "test data"));
|
||||
|
||||
final FieldDescriptor optionalField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("optional_nested_message");
|
||||
final Object value = NestedMessage.newBuilder().setBb(42).build();
|
||||
|
||||
assertEquals(
|
||||
"optional_nested_message {\n bb: 42\n}\n",
|
||||
TextFormat.printFieldToString(optionalField, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to construct a ByteString from a String containing only 8-bit
|
||||
* characters. The characters are converted directly to bytes, *not*
|
||||
* encoded using UTF-8.
|
||||
*/
|
||||
private ByteString bytes(String str) throws Exception {
|
||||
return ByteString.copyFrom(str.getBytes("ISO-8859-1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to construct a ByteString from a bunch of bytes. The inputs are
|
||||
* actually ints so that I can use hex notation and not get stupid errors
|
||||
* about precision.
|
||||
*/
|
||||
private ByteString bytes(int... bytesAsInts) {
|
||||
byte[] bytes = new byte[bytesAsInts.length];
|
||||
for (int i = 0; i < bytesAsInts.length; i++) {
|
||||
bytes[i] = (byte) bytesAsInts[i];
|
||||
}
|
||||
return ByteString.copyFrom(bytes);
|
||||
}
|
||||
|
||||
public void testPrintExotic() throws Exception {
|
||||
Message message = TestAllTypes.newBuilder()
|
||||
// Signed vs. unsigned numbers.
|
||||
.addRepeatedInt32 (-1)
|
||||
.addRepeatedUint32(-1)
|
||||
.addRepeatedInt64 (-1)
|
||||
.addRepeatedUint64(-1)
|
||||
|
||||
.addRepeatedInt32 (1 << 31)
|
||||
.addRepeatedUint32(1 << 31)
|
||||
.addRepeatedInt64 (1l << 63)
|
||||
.addRepeatedUint64(1l << 63)
|
||||
|
||||
// Floats of various precisions and exponents.
|
||||
.addRepeatedDouble(123)
|
||||
.addRepeatedDouble(123.5)
|
||||
.addRepeatedDouble(0.125)
|
||||
.addRepeatedDouble(.125)
|
||||
.addRepeatedDouble(-.125)
|
||||
.addRepeatedDouble(123e15)
|
||||
.addRepeatedDouble(123e15)
|
||||
.addRepeatedDouble(-1.23e-17)
|
||||
.addRepeatedDouble(.23e17)
|
||||
.addRepeatedDouble(-23e15)
|
||||
.addRepeatedDouble(123.5e20)
|
||||
.addRepeatedDouble(123.5e-20)
|
||||
.addRepeatedDouble(123.456789)
|
||||
.addRepeatedDouble(Double.POSITIVE_INFINITY)
|
||||
.addRepeatedDouble(Double.NEGATIVE_INFINITY)
|
||||
.addRepeatedDouble(Double.NaN)
|
||||
|
||||
// Strings and bytes that needing escaping.
|
||||
.addRepeatedString("\0\001\007\b\f\n\r\t\013\\\'\"\u1234")
|
||||
.addRepeatedBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\u00fe"))
|
||||
.build();
|
||||
|
||||
assertEquals(canonicalExoticText, message.toString());
|
||||
}
|
||||
|
||||
public void testPrintMessageSet() throws Exception {
|
||||
TestMessageSet messageSet =
|
||||
TestMessageSet.newBuilder()
|
||||
.setExtension(
|
||||
TestMessageSetExtension1.messageSetExtension,
|
||||
TestMessageSetExtension1.newBuilder().setI(123).build())
|
||||
.setExtension(
|
||||
TestMessageSetExtension2.messageSetExtension,
|
||||
TestMessageSetExtension2.newBuilder().setStr("foo").build())
|
||||
.build();
|
||||
|
||||
assertEquals(messageSetText, messageSet.toString());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
public void testParse() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(allFieldsSetText, builder);
|
||||
TestUtil.assertAllFieldsSet(builder.build());
|
||||
}
|
||||
|
||||
public void testParseReader() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(new StringReader(allFieldsSetText), builder);
|
||||
TestUtil.assertAllFieldsSet(builder.build());
|
||||
}
|
||||
|
||||
public void testParseExtensions() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
TextFormat.merge(allExtensionsSetText,
|
||||
TestUtil.getExtensionRegistry(),
|
||||
builder);
|
||||
TestUtil.assertAllExtensionsSet(builder.build());
|
||||
}
|
||||
|
||||
public void testParseCompatibility() throws Exception {
|
||||
String original = "repeated_float: inf\n" +
|
||||
"repeated_float: -inf\n" +
|
||||
"repeated_float: nan\n" +
|
||||
"repeated_float: inff\n" +
|
||||
"repeated_float: -inff\n" +
|
||||
"repeated_float: nanf\n" +
|
||||
"repeated_float: 1.0f\n" +
|
||||
"repeated_float: infinityf\n" +
|
||||
"repeated_float: -Infinityf\n" +
|
||||
"repeated_double: infinity\n" +
|
||||
"repeated_double: -infinity\n" +
|
||||
"repeated_double: nan\n";
|
||||
String canonical = "repeated_float: Infinity\n" +
|
||||
"repeated_float: -Infinity\n" +
|
||||
"repeated_float: NaN\n" +
|
||||
"repeated_float: Infinity\n" +
|
||||
"repeated_float: -Infinity\n" +
|
||||
"repeated_float: NaN\n" +
|
||||
"repeated_float: 1.0\n" +
|
||||
"repeated_float: Infinity\n" +
|
||||
"repeated_float: -Infinity\n" +
|
||||
"repeated_double: Infinity\n" +
|
||||
"repeated_double: -Infinity\n" +
|
||||
"repeated_double: NaN\n";
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(original, builder);
|
||||
assertEquals(canonical, builder.build().toString());
|
||||
}
|
||||
|
||||
public void testParseExotic() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(exoticText, builder);
|
||||
|
||||
// Too lazy to check things individually. Don't try to debug this
|
||||
// if testPrintExotic() is failing.
|
||||
assertEquals(canonicalExoticText, builder.build().toString());
|
||||
}
|
||||
|
||||
public void testParseMessageSet() throws Exception {
|
||||
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
|
||||
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
|
||||
extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
|
||||
|
||||
TestMessageSet.Builder builder = TestMessageSet.newBuilder();
|
||||
TextFormat.merge(messageSetText, extensionRegistry, builder);
|
||||
TestMessageSet messageSet = builder.build();
|
||||
|
||||
assertTrue(messageSet.hasExtension(
|
||||
TestMessageSetExtension1.messageSetExtension));
|
||||
assertEquals(123, messageSet.getExtension(
|
||||
TestMessageSetExtension1.messageSetExtension).getI());
|
||||
assertTrue(messageSet.hasExtension(
|
||||
TestMessageSetExtension2.messageSetExtension));
|
||||
assertEquals("foo", messageSet.getExtension(
|
||||
TestMessageSetExtension2.messageSetExtension).getStr());
|
||||
}
|
||||
|
||||
public void testParseNumericEnum() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_nested_enum: 2", builder);
|
||||
assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum());
|
||||
}
|
||||
|
||||
public void testParseAngleBrackets() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("OptionalGroup: < a: 1 >", builder);
|
||||
assertTrue(builder.hasOptionalGroup());
|
||||
assertEquals(1, builder.getOptionalGroup().getA());
|
||||
}
|
||||
|
||||
public void testParseComment() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(
|
||||
"# this is a comment\n" +
|
||||
"optional_int32: 1 # another comment\n" +
|
||||
"optional_int64: 2\n" +
|
||||
"# EOF comment", builder);
|
||||
assertEquals(1, builder.getOptionalInt32());
|
||||
assertEquals(2, builder.getOptionalInt64());
|
||||
}
|
||||
|
||||
private void assertParseError(String error, String text) {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
try {
|
||||
TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder);
|
||||
fail("Expected parse exception.");
|
||||
} catch (TextFormat.ParseException e) {
|
||||
assertEquals(error, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseErrors() throws Exception {
|
||||
assertParseError(
|
||||
"1:16: Expected \":\".",
|
||||
"optional_int32 123");
|
||||
assertParseError(
|
||||
"1:23: Expected identifier.",
|
||||
"optional_nested_enum: ?");
|
||||
assertParseError(
|
||||
"1:18: Couldn't parse integer: Number must be positive: -1",
|
||||
"optional_uint32: -1");
|
||||
assertParseError(
|
||||
"1:17: Couldn't parse integer: Number out of range for 32-bit signed " +
|
||||
"integer: 82301481290849012385230157",
|
||||
"optional_int32: 82301481290849012385230157");
|
||||
assertParseError(
|
||||
"1:16: Expected \"true\" or \"false\".",
|
||||
"optional_bool: maybe");
|
||||
assertParseError(
|
||||
"1:16: Expected \"true\" or \"false\".",
|
||||
"optional_bool: 2");
|
||||
assertParseError(
|
||||
"1:18: Expected string.",
|
||||
"optional_string: 123");
|
||||
assertParseError(
|
||||
"1:18: String missing ending quote.",
|
||||
"optional_string: \"ueoauaoe");
|
||||
assertParseError(
|
||||
"1:18: String missing ending quote.",
|
||||
"optional_string: \"ueoauaoe\n" +
|
||||
"optional_int32: 123");
|
||||
assertParseError(
|
||||
"1:18: Invalid escape sequence: '\\z'",
|
||||
"optional_string: \"\\z\"");
|
||||
assertParseError(
|
||||
"1:18: String missing ending quote.",
|
||||
"optional_string: \"ueoauaoe\n" +
|
||||
"optional_int32: 123");
|
||||
assertParseError(
|
||||
"1:2: Extension \"nosuchext\" not found in the ExtensionRegistry.",
|
||||
"[nosuchext]: 123");
|
||||
assertParseError(
|
||||
"1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " +
|
||||
"not extend message type \"protobuf_unittest.TestAllTypes\".",
|
||||
"[protobuf_unittest.optional_int32_extension]: 123");
|
||||
assertParseError(
|
||||
"1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " +
|
||||
"named \"nosuchfield\".",
|
||||
"nosuchfield: 123");
|
||||
assertParseError(
|
||||
"1:21: Expected \">\".",
|
||||
"OptionalGroup < a: 1");
|
||||
assertParseError(
|
||||
"1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " +
|
||||
"value named \"NO_SUCH_VALUE\".",
|
||||
"optional_nested_enum: NO_SUCH_VALUE");
|
||||
assertParseError(
|
||||
"1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " +
|
||||
"value with number 123.",
|
||||
"optional_nested_enum: 123");
|
||||
|
||||
// Delimiters must match.
|
||||
assertParseError(
|
||||
"1:22: Expected identifier.",
|
||||
"OptionalGroup < a: 1 }");
|
||||
assertParseError(
|
||||
"1:22: Expected identifier.",
|
||||
"OptionalGroup { a: 1 >");
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
public void testEscape() throws Exception {
|
||||
// Escape sequences.
|
||||
assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"",
|
||||
TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"")));
|
||||
assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"",
|
||||
TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\""));
|
||||
assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""),
|
||||
TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
|
||||
assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"",
|
||||
TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
|
||||
assertEquals(kEscapeTestStringEscaped,
|
||||
TextFormat.escapeText(kEscapeTestString));
|
||||
assertEquals(kEscapeTestString,
|
||||
TextFormat.unescapeText(kEscapeTestStringEscaped));
|
||||
|
||||
// Unicode handling.
|
||||
assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234"));
|
||||
assertEquals("\\341\\210\\264",
|
||||
TextFormat.escapeBytes(bytes(0xe1, 0x88, 0xb4)));
|
||||
assertEquals("\u1234", TextFormat.unescapeText("\\341\\210\\264"));
|
||||
assertEquals(bytes(0xe1, 0x88, 0xb4),
|
||||
TextFormat.unescapeBytes("\\341\\210\\264"));
|
||||
assertEquals("\u1234", TextFormat.unescapeText("\\xe1\\x88\\xb4"));
|
||||
assertEquals(bytes(0xe1, 0x88, 0xb4),
|
||||
TextFormat.unescapeBytes("\\xe1\\x88\\xb4"));
|
||||
|
||||
// Handling of strings with unescaped Unicode characters > 255.
|
||||
final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
|
||||
ByteString zhByteString = ByteString.copyFromUtf8(zh);
|
||||
assertEquals(zhByteString, TextFormat.unescapeBytes(zh));
|
||||
|
||||
// Errors.
|
||||
try {
|
||||
TextFormat.unescapeText("\\x");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.InvalidEscapeSequenceException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.unescapeText("\\z");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.InvalidEscapeSequenceException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.unescapeText("\\");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.InvalidEscapeSequenceException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseInteger() throws Exception {
|
||||
assertEquals( 0, TextFormat.parseInt32( "0"));
|
||||
assertEquals( 1, TextFormat.parseInt32( "1"));
|
||||
assertEquals( -1, TextFormat.parseInt32( "-1"));
|
||||
assertEquals( 12345, TextFormat.parseInt32( "12345"));
|
||||
assertEquals( -12345, TextFormat.parseInt32( "-12345"));
|
||||
assertEquals( 2147483647, TextFormat.parseInt32( "2147483647"));
|
||||
assertEquals(-2147483648, TextFormat.parseInt32("-2147483648"));
|
||||
|
||||
assertEquals( 0, TextFormat.parseUInt32( "0"));
|
||||
assertEquals( 1, TextFormat.parseUInt32( "1"));
|
||||
assertEquals( 12345, TextFormat.parseUInt32( "12345"));
|
||||
assertEquals( 2147483647, TextFormat.parseUInt32("2147483647"));
|
||||
assertEquals((int) 2147483648L, TextFormat.parseUInt32("2147483648"));
|
||||
assertEquals((int) 4294967295L, TextFormat.parseUInt32("4294967295"));
|
||||
|
||||
assertEquals( 0L, TextFormat.parseInt64( "0"));
|
||||
assertEquals( 1L, TextFormat.parseInt64( "1"));
|
||||
assertEquals( -1L, TextFormat.parseInt64( "-1"));
|
||||
assertEquals( 12345L, TextFormat.parseInt64( "12345"));
|
||||
assertEquals( -12345L, TextFormat.parseInt64( "-12345"));
|
||||
assertEquals( 2147483647L, TextFormat.parseInt64( "2147483647"));
|
||||
assertEquals(-2147483648L, TextFormat.parseInt64("-2147483648"));
|
||||
assertEquals( 4294967295L, TextFormat.parseInt64( "4294967295"));
|
||||
assertEquals( 4294967296L, TextFormat.parseInt64( "4294967296"));
|
||||
assertEquals(9223372036854775807L,
|
||||
TextFormat.parseInt64("9223372036854775807"));
|
||||
assertEquals(-9223372036854775808L,
|
||||
TextFormat.parseInt64("-9223372036854775808"));
|
||||
|
||||
assertEquals( 0L, TextFormat.parseUInt64( "0"));
|
||||
assertEquals( 1L, TextFormat.parseUInt64( "1"));
|
||||
assertEquals( 12345L, TextFormat.parseUInt64( "12345"));
|
||||
assertEquals( 2147483647L, TextFormat.parseUInt64( "2147483647"));
|
||||
assertEquals( 4294967295L, TextFormat.parseUInt64( "4294967295"));
|
||||
assertEquals( 4294967296L, TextFormat.parseUInt64( "4294967296"));
|
||||
assertEquals(9223372036854775807L,
|
||||
TextFormat.parseUInt64("9223372036854775807"));
|
||||
assertEquals(-9223372036854775808L,
|
||||
TextFormat.parseUInt64("9223372036854775808"));
|
||||
assertEquals(-1L, TextFormat.parseUInt64("18446744073709551615"));
|
||||
|
||||
// Hex
|
||||
assertEquals(0x1234abcd, TextFormat.parseInt32("0x1234abcd"));
|
||||
assertEquals(-0x1234abcd, TextFormat.parseInt32("-0x1234abcd"));
|
||||
assertEquals(-1, TextFormat.parseUInt64("0xffffffffffffffff"));
|
||||
assertEquals(0x7fffffffffffffffL,
|
||||
TextFormat.parseInt64("0x7fffffffffffffff"));
|
||||
|
||||
// Octal
|
||||
assertEquals(01234567, TextFormat.parseInt32("01234567"));
|
||||
|
||||
// Out-of-range
|
||||
try {
|
||||
TextFormat.parseInt32("2147483648");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseInt32("-2147483649");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseUInt32("4294967296");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseUInt32("-1");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseInt64("9223372036854775808");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseInt64("-9223372036854775809");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseUInt64("18446744073709551616");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
try {
|
||||
TextFormat.parseUInt64("-1");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
|
||||
// Not a number.
|
||||
try {
|
||||
TextFormat.parseInt32("abcd");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (NumberFormatException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseString() throws Exception {
|
||||
final String zh = "\u9999\u6e2f\u4e0a\u6d77\ud84f\udf80\u8c50\u9280\u884c";
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_string: \"" + zh + "\"", builder);
|
||||
assertEquals(zh, builder.getOptionalString());
|
||||
}
|
||||
|
||||
public void testParseLongString() throws Exception {
|
||||
String longText =
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012345678901234567890123456789012345678901234567890";
|
||||
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_string: \"" + longText + "\"", builder);
|
||||
assertEquals(longText, builder.getOptionalString());
|
||||
}
|
||||
|
||||
public void testParseBoolean() throws Exception {
|
||||
String goodText =
|
||||
"repeated_bool: t repeated_bool : 0\n" +
|
||||
"repeated_bool :f repeated_bool:1";
|
||||
String goodTextCanonical =
|
||||
"repeated_bool: true\n" +
|
||||
"repeated_bool: false\n" +
|
||||
"repeated_bool: false\n" +
|
||||
"repeated_bool: true\n";
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge(goodText, builder);
|
||||
assertEquals(goodTextCanonical, builder.build().toString());
|
||||
|
||||
try {
|
||||
TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_bool:2", badBuilder);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.ParseException e) {
|
||||
// success
|
||||
}
|
||||
try {
|
||||
TestAllTypes.Builder badBuilder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_bool: foo", badBuilder);
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (TextFormat.ParseException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseAdjacentStringLiterals() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder);
|
||||
assertEquals("foocorgegrault", builder.getOptionalString());
|
||||
}
|
||||
|
||||
public void testPrintFieldValue() throws Exception {
|
||||
assertPrintFieldValue("\"Hello\"", "Hello", "repeated_string");
|
||||
assertPrintFieldValue("123.0", 123f, "repeated_float");
|
||||
assertPrintFieldValue("123.0", 123d, "repeated_double");
|
||||
assertPrintFieldValue("123", 123, "repeated_int32");
|
||||
assertPrintFieldValue("123", 123L, "repeated_int64");
|
||||
assertPrintFieldValue("true", true, "repeated_bool");
|
||||
assertPrintFieldValue("4294967295", 0xFFFFFFFF, "repeated_uint32");
|
||||
assertPrintFieldValue("18446744073709551615", 0xFFFFFFFFFFFFFFFFL,
|
||||
"repeated_uint64");
|
||||
assertPrintFieldValue("\"\\001\\002\\003\"",
|
||||
ByteString.copyFrom(new byte[] {1, 2, 3}), "repeated_bytes");
|
||||
}
|
||||
|
||||
private void assertPrintFieldValue(String expect, Object value,
|
||||
String fieldName) throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
TextFormat.printFieldValue(
|
||||
TestAllTypes.getDescriptor().findFieldByName(fieldName),
|
||||
value, sb);
|
||||
assertEquals(expect, sb.toString());
|
||||
}
|
||||
|
||||
public void testShortDebugString() {
|
||||
assertEquals("optional_nested_message { bb: 42 } repeated_int32: 1"
|
||||
+ " repeated_uint32: 2",
|
||||
TextFormat.shortDebugString(TestAllTypes.newBuilder()
|
||||
.addRepeatedInt32(1)
|
||||
.addRepeatedUint32(2)
|
||||
.setOptionalNestedMessage(
|
||||
NestedMessage.newBuilder().setBb(42).build())
|
||||
.build()));
|
||||
}
|
||||
|
||||
public void testShortDebugString_unknown() {
|
||||
assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }"
|
||||
+ " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:"
|
||||
+ " 0xabcdef1234567890",
|
||||
TextFormat.shortDebugString(makeUnknownFieldSet()));
|
||||
}
|
||||
|
||||
public void testPrintToUnicodeString() {
|
||||
assertEquals(
|
||||
"optional_string: \"abc\u3042efg\"\n" +
|
||||
"optional_bytes: \"\\343\\201\\202\"\n" +
|
||||
"repeated_string: \"\u3093XYZ\"\n",
|
||||
TextFormat.printToUnicodeString(TestAllTypes.newBuilder()
|
||||
.setOptionalString("abc\u3042efg")
|
||||
.setOptionalBytes(bytes(0xe3, 0x81, 0x82))
|
||||
.addRepeatedString("\u3093XYZ")
|
||||
.build()));
|
||||
}
|
||||
|
||||
public void testPrintToUnicodeString_unknown() {
|
||||
assertEquals(
|
||||
"1: \"\\343\\201\\202\"\n",
|
||||
TextFormat.printToUnicodeString(UnknownFieldSet.newBuilder()
|
||||
.addField(1,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addLengthDelimited(bytes(0xe3, 0x81, 0x82)).build())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
437
java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
Normal file
437
java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
Normal file
@@ -0,0 +1,437 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tests related to unknown field handling.
|
||||
*
|
||||
* @author kenton@google.com (Kenton Varda)
|
||||
*/
|
||||
public class UnknownFieldSetTest extends TestCase {
|
||||
public void setUp() throws Exception {
|
||||
descriptor = TestAllTypes.getDescriptor();
|
||||
allFields = TestUtil.getAllSet();
|
||||
allFieldsData = allFields.toByteString();
|
||||
emptyMessage = TestEmptyMessage.parseFrom(allFieldsData);
|
||||
unknownFields = emptyMessage.getUnknownFields();
|
||||
}
|
||||
|
||||
UnknownFieldSet.Field getField(String name) {
|
||||
Descriptors.FieldDescriptor field = descriptor.findFieldByName(name);
|
||||
assertNotNull(field);
|
||||
return unknownFields.getField(field.getNumber());
|
||||
}
|
||||
|
||||
// Constructs a protocol buffer which contains fields with all the same
|
||||
// numbers as allFieldsData except that each field is some other wire
|
||||
// type.
|
||||
ByteString getBizarroData() throws Exception {
|
||||
UnknownFieldSet.Builder bizarroFields = UnknownFieldSet.newBuilder();
|
||||
|
||||
UnknownFieldSet.Field varintField =
|
||||
UnknownFieldSet.Field.newBuilder().addVarint(1).build();
|
||||
UnknownFieldSet.Field fixed32Field =
|
||||
UnknownFieldSet.Field.newBuilder().addFixed32(1).build();
|
||||
|
||||
for (Map.Entry<Integer, UnknownFieldSet.Field> entry :
|
||||
unknownFields.asMap().entrySet()) {
|
||||
if (entry.getValue().getVarintList().isEmpty()) {
|
||||
// Original field is not a varint, so use a varint.
|
||||
bizarroFields.addField(entry.getKey(), varintField);
|
||||
} else {
|
||||
// Original field *is* a varint, so use something else.
|
||||
bizarroFields.addField(entry.getKey(), fixed32Field);
|
||||
}
|
||||
}
|
||||
|
||||
return bizarroFields.build().toByteString();
|
||||
}
|
||||
|
||||
Descriptors.Descriptor descriptor;
|
||||
TestAllTypes allFields;
|
||||
ByteString allFieldsData;
|
||||
|
||||
// An empty message that has been parsed from allFieldsData. So, it has
|
||||
// unknown fields of every type.
|
||||
TestEmptyMessage emptyMessage;
|
||||
UnknownFieldSet unknownFields;
|
||||
|
||||
// =================================================================
|
||||
|
||||
public void testVarint() throws Exception {
|
||||
UnknownFieldSet.Field field = getField("optional_int32");
|
||||
assertEquals(1, field.getVarintList().size());
|
||||
assertEquals(allFields.getOptionalInt32(),
|
||||
(long) field.getVarintList().get(0));
|
||||
}
|
||||
|
||||
public void testFixed32() throws Exception {
|
||||
UnknownFieldSet.Field field = getField("optional_fixed32");
|
||||
assertEquals(1, field.getFixed32List().size());
|
||||
assertEquals(allFields.getOptionalFixed32(),
|
||||
(int) field.getFixed32List().get(0));
|
||||
}
|
||||
|
||||
public void testFixed64() throws Exception {
|
||||
UnknownFieldSet.Field field = getField("optional_fixed64");
|
||||
assertEquals(1, field.getFixed64List().size());
|
||||
assertEquals(allFields.getOptionalFixed64(),
|
||||
(long) field.getFixed64List().get(0));
|
||||
}
|
||||
|
||||
public void testLengthDelimited() throws Exception {
|
||||
UnknownFieldSet.Field field = getField("optional_bytes");
|
||||
assertEquals(1, field.getLengthDelimitedList().size());
|
||||
assertEquals(allFields.getOptionalBytes(),
|
||||
field.getLengthDelimitedList().get(0));
|
||||
}
|
||||
|
||||
public void testGroup() throws Exception {
|
||||
Descriptors.FieldDescriptor nestedFieldDescriptor =
|
||||
TestAllTypes.OptionalGroup.getDescriptor().findFieldByName("a");
|
||||
assertNotNull(nestedFieldDescriptor);
|
||||
|
||||
UnknownFieldSet.Field field = getField("optionalgroup");
|
||||
assertEquals(1, field.getGroupList().size());
|
||||
|
||||
UnknownFieldSet group = field.getGroupList().get(0);
|
||||
assertEquals(1, group.asMap().size());
|
||||
assertTrue(group.hasField(nestedFieldDescriptor.getNumber()));
|
||||
|
||||
UnknownFieldSet.Field nestedField =
|
||||
group.getField(nestedFieldDescriptor.getNumber());
|
||||
assertEquals(1, nestedField.getVarintList().size());
|
||||
assertEquals(allFields.getOptionalGroup().getA(),
|
||||
(long) nestedField.getVarintList().get(0));
|
||||
}
|
||||
|
||||
public void testSerialize() throws Exception {
|
||||
// Check that serializing the UnknownFieldSet produces the original data
|
||||
// again.
|
||||
ByteString data = emptyMessage.toByteString();
|
||||
assertEquals(allFieldsData, data);
|
||||
}
|
||||
|
||||
public void testCopyFrom() throws Exception {
|
||||
TestEmptyMessage message =
|
||||
TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).build();
|
||||
|
||||
assertEquals(emptyMessage.toString(), message.toString());
|
||||
}
|
||||
|
||||
public void testMergeFrom() throws Exception {
|
||||
TestEmptyMessage source =
|
||||
TestEmptyMessage.newBuilder()
|
||||
.setUnknownFields(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(2,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(2).build())
|
||||
.addField(3,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(4).build())
|
||||
.build())
|
||||
.build();
|
||||
TestEmptyMessage destination =
|
||||
TestEmptyMessage.newBuilder()
|
||||
.setUnknownFields(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1).build())
|
||||
.addField(3,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(3).build())
|
||||
.build())
|
||||
.mergeFrom(source)
|
||||
.build();
|
||||
|
||||
assertEquals(
|
||||
"1: 1\n" +
|
||||
"2: 2\n" +
|
||||
"3: 3\n" +
|
||||
"3: 4\n",
|
||||
destination.toString());
|
||||
}
|
||||
|
||||
public void testClear() throws Exception {
|
||||
UnknownFieldSet fields =
|
||||
UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build();
|
||||
assertTrue(fields.asMap().isEmpty());
|
||||
}
|
||||
|
||||
public void testClearMessage() throws Exception {
|
||||
TestEmptyMessage message =
|
||||
TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).clear().build();
|
||||
assertEquals(0, message.getSerializedSize());
|
||||
}
|
||||
|
||||
public void testParseKnownAndUnknown() throws Exception {
|
||||
// Test mixing known and unknown fields when parsing.
|
||||
|
||||
UnknownFieldSet fields =
|
||||
UnknownFieldSet.newBuilder(unknownFields)
|
||||
.addField(123456,
|
||||
UnknownFieldSet.Field.newBuilder().addVarint(654321).build())
|
||||
.build();
|
||||
|
||||
ByteString data = fields.toByteString();
|
||||
TestAllTypes destination = TestAllTypes.parseFrom(data);
|
||||
|
||||
TestUtil.assertAllFieldsSet(destination);
|
||||
assertEquals(1, destination.getUnknownFields().asMap().size());
|
||||
|
||||
UnknownFieldSet.Field field =
|
||||
destination.getUnknownFields().getField(123456);
|
||||
assertEquals(1, field.getVarintList().size());
|
||||
assertEquals(654321, (long) field.getVarintList().get(0));
|
||||
}
|
||||
|
||||
public void testWrongTypeTreatedAsUnknown() throws Exception {
|
||||
// Test that fields of the wrong wire type are treated like unknown fields
|
||||
// when parsing.
|
||||
|
||||
ByteString bizarroData = getBizarroData();
|
||||
TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData);
|
||||
TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData);
|
||||
|
||||
// All fields should have been interpreted as unknown, so the debug strings
|
||||
// should be the same.
|
||||
assertEquals(emptyMessage.toString(), allTypesMessage.toString());
|
||||
}
|
||||
|
||||
public void testUnknownExtensions() throws Exception {
|
||||
// Make sure fields are properly parsed to the UnknownFieldSet even when
|
||||
// they are declared as extension numbers.
|
||||
|
||||
TestEmptyMessageWithExtensions message =
|
||||
TestEmptyMessageWithExtensions.parseFrom(allFieldsData);
|
||||
|
||||
assertEquals(unknownFields.asMap().size(),
|
||||
message.getUnknownFields().asMap().size());
|
||||
assertEquals(allFieldsData, message.toByteString());
|
||||
}
|
||||
|
||||
public void testWrongExtensionTypeTreatedAsUnknown() throws Exception {
|
||||
// Test that fields of the wrong wire type are treated like unknown fields
|
||||
// when parsing extensions.
|
||||
|
||||
ByteString bizarroData = getBizarroData();
|
||||
TestAllExtensions allExtensionsMessage =
|
||||
TestAllExtensions.parseFrom(bizarroData);
|
||||
TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData);
|
||||
|
||||
// All fields should have been interpreted as unknown, so the debug strings
|
||||
// should be the same.
|
||||
assertEquals(emptyMessage.toString(),
|
||||
allExtensionsMessage.toString());
|
||||
}
|
||||
|
||||
public void testParseUnknownEnumValue() throws Exception {
|
||||
Descriptors.FieldDescriptor singularField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum");
|
||||
Descriptors.FieldDescriptor repeatedField =
|
||||
TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum");
|
||||
assertNotNull(singularField);
|
||||
assertNotNull(repeatedField);
|
||||
|
||||
ByteString data =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(singularField.getNumber(),
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(TestAllTypes.NestedEnum.BAR.getNumber())
|
||||
.addVarint(5) // not valid
|
||||
.build())
|
||||
.addField(repeatedField.getNumber(),
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(TestAllTypes.NestedEnum.FOO.getNumber())
|
||||
.addVarint(4) // not valid
|
||||
.addVarint(TestAllTypes.NestedEnum.BAZ.getNumber())
|
||||
.addVarint(6) // not valid
|
||||
.build())
|
||||
.build()
|
||||
.toByteString();
|
||||
|
||||
{
|
||||
TestAllTypes message = TestAllTypes.parseFrom(data);
|
||||
assertEquals(TestAllTypes.NestedEnum.BAR,
|
||||
message.getOptionalNestedEnum());
|
||||
assertEquals(
|
||||
Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
|
||||
message.getRepeatedNestedEnumList());
|
||||
assertEquals(Arrays.asList(5L),
|
||||
message.getUnknownFields()
|
||||
.getField(singularField.getNumber())
|
||||
.getVarintList());
|
||||
assertEquals(Arrays.asList(4L, 6L),
|
||||
message.getUnknownFields()
|
||||
.getField(repeatedField.getNumber())
|
||||
.getVarintList());
|
||||
}
|
||||
|
||||
{
|
||||
TestAllExtensions message =
|
||||
TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
|
||||
assertEquals(TestAllTypes.NestedEnum.BAR,
|
||||
message.getExtension(UnittestProto.optionalNestedEnumExtension));
|
||||
assertEquals(
|
||||
Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
|
||||
message.getExtension(UnittestProto.repeatedNestedEnumExtension));
|
||||
assertEquals(Arrays.asList(5L),
|
||||
message.getUnknownFields()
|
||||
.getField(singularField.getNumber())
|
||||
.getVarintList());
|
||||
assertEquals(Arrays.asList(4L, 6L),
|
||||
message.getUnknownFields()
|
||||
.getField(repeatedField.getNumber())
|
||||
.getVarintList());
|
||||
}
|
||||
}
|
||||
|
||||
public void testLargeVarint() throws Exception {
|
||||
ByteString data =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(0x7FFFFFFFFFFFFFFFL)
|
||||
.build())
|
||||
.build()
|
||||
.toByteString();
|
||||
UnknownFieldSet parsed = UnknownFieldSet.parseFrom(data);
|
||||
UnknownFieldSet.Field field = parsed.getField(1);
|
||||
assertEquals(1, field.getVarintList().size());
|
||||
assertEquals(0x7FFFFFFFFFFFFFFFL, (long)field.getVarintList().get(0));
|
||||
}
|
||||
|
||||
public void testEqualsAndHashCode() {
|
||||
UnknownFieldSet.Field fixed32Field =
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addFixed32(1)
|
||||
.build();
|
||||
UnknownFieldSet.Field fixed64Field =
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addFixed64(1)
|
||||
.build();
|
||||
UnknownFieldSet.Field varIntField =
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addVarint(1)
|
||||
.build();
|
||||
UnknownFieldSet.Field lengthDelimitedField =
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addLengthDelimited(ByteString.EMPTY)
|
||||
.build();
|
||||
UnknownFieldSet.Field groupField =
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addGroup(unknownFields)
|
||||
.build();
|
||||
|
||||
UnknownFieldSet a =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1, fixed32Field)
|
||||
.build();
|
||||
UnknownFieldSet b =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1, fixed64Field)
|
||||
.build();
|
||||
UnknownFieldSet c =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1, varIntField)
|
||||
.build();
|
||||
UnknownFieldSet d =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1, lengthDelimitedField)
|
||||
.build();
|
||||
UnknownFieldSet e =
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(1, groupField)
|
||||
.build();
|
||||
|
||||
checkEqualsIsConsistent(a);
|
||||
checkEqualsIsConsistent(b);
|
||||
checkEqualsIsConsistent(c);
|
||||
checkEqualsIsConsistent(d);
|
||||
checkEqualsIsConsistent(e);
|
||||
|
||||
checkNotEqual(a, b);
|
||||
checkNotEqual(a, c);
|
||||
checkNotEqual(a, d);
|
||||
checkNotEqual(a, e);
|
||||
checkNotEqual(b, c);
|
||||
checkNotEqual(b, d);
|
||||
checkNotEqual(b, e);
|
||||
checkNotEqual(c, d);
|
||||
checkNotEqual(c, e);
|
||||
checkNotEqual(d, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given field sets are not equal and have different
|
||||
* hash codes.
|
||||
*
|
||||
* @warning It's valid for non-equal objects to have the same hash code, so
|
||||
* this test is stricter than it needs to be. However, this should happen
|
||||
* relatively rarely.
|
||||
*/
|
||||
private void checkNotEqual(UnknownFieldSet s1, UnknownFieldSet s2) {
|
||||
String equalsError = String.format("%s should not be equal to %s", s1, s2);
|
||||
assertFalse(equalsError, s1.equals(s2));
|
||||
assertFalse(equalsError, s2.equals(s1));
|
||||
|
||||
assertFalse(
|
||||
String.format("%s should have a different hash code from %s", s1, s2),
|
||||
s1.hashCode() == s2.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given field sets are equal and have identical hash codes.
|
||||
*/
|
||||
private void checkEqualsIsConsistent(UnknownFieldSet set) {
|
||||
// Object should be equal to itself.
|
||||
assertEquals(set, set);
|
||||
|
||||
// Object should be equal to a copy of itself.
|
||||
UnknownFieldSet copy = UnknownFieldSet.newBuilder(set).build();
|
||||
assertEquals(set, copy);
|
||||
assertEquals(copy, set);
|
||||
assertEquals(set.hashCode(), copy.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* Tests for {@link UnmodifiableLazyStringList}.
|
||||
*
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
*/
|
||||
public class UnmodifiableLazyStringListTest extends TestCase {
|
||||
|
||||
private static String STRING_A = "A";
|
||||
private static String STRING_B = "B";
|
||||
private static String STRING_C = "C";
|
||||
|
||||
private static ByteString BYTE_STRING_A = ByteString.copyFromUtf8("A");
|
||||
private static ByteString BYTE_STRING_B = ByteString.copyFromUtf8("B");
|
||||
private static ByteString BYTE_STRING_C = ByteString.copyFromUtf8("C");
|
||||
|
||||
public void testReadOnlyMethods() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
assertEquals(3, list.size());
|
||||
assertSame(STRING_A, list.get(0));
|
||||
assertSame(STRING_B, list.get(1));
|
||||
assertSame(STRING_C, list.get(2));
|
||||
assertEquals(BYTE_STRING_A, list.getByteString(0));
|
||||
assertEquals(BYTE_STRING_B, list.getByteString(1));
|
||||
assertEquals(BYTE_STRING_C, list.getByteString(2));
|
||||
}
|
||||
|
||||
public void testModifyMethods() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
|
||||
try {
|
||||
list.remove(0);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
assertEquals(3, list.size());
|
||||
|
||||
try {
|
||||
list.add(STRING_B);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
assertEquals(3, list.size());
|
||||
|
||||
try {
|
||||
list.set(1, STRING_B);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testIterator() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
|
||||
Iterator<String> iter = list.iterator();
|
||||
int count = 0;
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
count++;
|
||||
try {
|
||||
iter.remove();
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
assertEquals(3, count);
|
||||
|
||||
}
|
||||
|
||||
public void testListIterator() {
|
||||
LazyStringArrayList rawList = createSampleList();
|
||||
UnmodifiableLazyStringList list = new UnmodifiableLazyStringList(rawList);
|
||||
|
||||
ListIterator<String> iter = list.listIterator();
|
||||
int count = 0;
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
count++;
|
||||
try {
|
||||
iter.remove();
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
iter.set("bar");
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
iter.add("bar");
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
assertEquals(3, count);
|
||||
|
||||
}
|
||||
|
||||
private LazyStringArrayList createSampleList() {
|
||||
LazyStringArrayList rawList = new LazyStringArrayList();
|
||||
rawList.add(STRING_A);
|
||||
rawList.add(STRING_B);
|
||||
rawList.add(STRING_C);
|
||||
return rawList;
|
||||
}
|
||||
}
|
||||
580
java/src/test/java/com/google/protobuf/WireFormatTest.java
Normal file
580
java/src/test/java/com/google/protobuf/WireFormatTest.java
Normal file
@@ -0,0 +1,580 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestFieldOrderings;
|
||||
import protobuf_unittest.UnittestProto.TestPackedExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSet;
|
||||
import protobuf_unittest.UnittestMset.RawMessageSet;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
|
||||
/**
|
||||
* Tests related to parsing and serialization.
|
||||
*
|
||||
* @author kenton@google.com (Kenton Varda)
|
||||
*/
|
||||
public class WireFormatTest extends TestCase {
|
||||
public void testSerialization() throws Exception {
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
assertEquals(rawBytes.size(), message.getSerializedSize());
|
||||
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
}
|
||||
|
||||
public void testSerializationPacked() throws Exception {
|
||||
TestPackedTypes message = TestUtil.getPackedSet();
|
||||
|
||||
ByteString rawBytes = message.toByteString();
|
||||
assertEquals(rawBytes.size(), message.getSerializedSize());
|
||||
|
||||
TestPackedTypes message2 = TestPackedTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertPackedFieldsSet(message2);
|
||||
}
|
||||
|
||||
public void testSerializeExtensions() throws Exception {
|
||||
// TestAllTypes and TestAllExtensions should have compatible wire formats,
|
||||
// so if we serialize a TestAllExtensions then parse it as TestAllTypes
|
||||
// it should work.
|
||||
|
||||
TestAllExtensions message = TestUtil.getAllExtensionsSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
assertEquals(rawBytes.size(), message.getSerializedSize());
|
||||
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
}
|
||||
|
||||
public void testSerializePackedExtensions() throws Exception {
|
||||
// TestPackedTypes and TestPackedExtensions should have compatible wire
|
||||
// formats; check that they serialize to the same string.
|
||||
TestPackedExtensions message = TestUtil.getPackedExtensionsSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
TestPackedTypes message2 = TestUtil.getPackedSet();
|
||||
ByteString rawBytes2 = message2.toByteString();
|
||||
|
||||
assertEquals(rawBytes, rawBytes2);
|
||||
}
|
||||
|
||||
public void testSerializationPackedWithoutGetSerializedSize()
|
||||
throws Exception {
|
||||
// Write directly to an OutputStream, without invoking getSerializedSize()
|
||||
// This used to be a bug where the size of a packed field was incorrect,
|
||||
// since getSerializedSize() was never invoked.
|
||||
TestPackedTypes message = TestUtil.getPackedSet();
|
||||
|
||||
// Directly construct a CodedOutputStream around the actual OutputStream,
|
||||
// in case writeTo(OutputStream output) invokes getSerializedSize();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
CodedOutputStream codedOutput = CodedOutputStream.newInstance(outputStream);
|
||||
|
||||
message.writeTo(codedOutput);
|
||||
|
||||
codedOutput.flush();
|
||||
|
||||
TestPackedTypes message2 = TestPackedTypes.parseFrom(
|
||||
outputStream.toByteArray());
|
||||
|
||||
TestUtil.assertPackedFieldsSet(message2);
|
||||
}
|
||||
|
||||
public void testSerializeExtensionsLite() throws Exception {
|
||||
// TestAllTypes and TestAllExtensions should have compatible wire formats,
|
||||
// so if we serialize a TestAllExtensions then parse it as TestAllTypes
|
||||
// it should work.
|
||||
|
||||
TestAllExtensionsLite message = TestUtil.getAllLiteExtensionsSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
assertEquals(rawBytes.size(), message.getSerializedSize());
|
||||
|
||||
TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
|
||||
|
||||
TestUtil.assertAllFieldsSet(message2);
|
||||
}
|
||||
|
||||
public void testSerializePackedExtensionsLite() throws Exception {
|
||||
// TestPackedTypes and TestPackedExtensions should have compatible wire
|
||||
// formats; check that they serialize to the same string.
|
||||
TestPackedExtensionsLite message = TestUtil.getLitePackedExtensionsSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
TestPackedTypes message2 = TestUtil.getPackedSet();
|
||||
ByteString rawBytes2 = message2.toByteString();
|
||||
|
||||
assertEquals(rawBytes, rawBytes2);
|
||||
}
|
||||
|
||||
public void testParseExtensions() throws Exception {
|
||||
// TestAllTypes and TestAllExtensions should have compatible wire formats,
|
||||
// so if we serialize a TestAllTypes then parse it as TestAllExtensions
|
||||
// it should work.
|
||||
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
ExtensionRegistry registry = TestUtil.getExtensionRegistry();
|
||||
|
||||
TestAllExtensions message2 =
|
||||
TestAllExtensions.parseFrom(rawBytes, registry);
|
||||
|
||||
TestUtil.assertAllExtensionsSet(message2);
|
||||
}
|
||||
|
||||
public void testParsePackedExtensions() throws Exception {
|
||||
// Ensure that packed extensions can be properly parsed.
|
||||
TestPackedExtensions message = TestUtil.getPackedExtensionsSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
ExtensionRegistry registry = TestUtil.getExtensionRegistry();
|
||||
|
||||
TestPackedExtensions message2 =
|
||||
TestPackedExtensions.parseFrom(rawBytes, registry);
|
||||
|
||||
TestUtil.assertPackedExtensionsSet(message2);
|
||||
}
|
||||
|
||||
public void testParseExtensionsLite() throws Exception {
|
||||
// TestAllTypes and TestAllExtensions should have compatible wire formats,
|
||||
// so if we serialize a TestAllTypes then parse it as TestAllExtensions
|
||||
// it should work.
|
||||
|
||||
TestAllTypes message = TestUtil.getAllSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
ExtensionRegistryLite registry_lite = TestUtil.getExtensionRegistryLite();
|
||||
|
||||
TestAllExtensionsLite message2 =
|
||||
TestAllExtensionsLite.parseFrom(rawBytes, registry_lite);
|
||||
|
||||
TestUtil.assertAllExtensionsSet(message2);
|
||||
|
||||
// Try again using a full extension registry.
|
||||
ExtensionRegistry registry = TestUtil.getExtensionRegistry();
|
||||
|
||||
TestAllExtensionsLite message3 =
|
||||
TestAllExtensionsLite.parseFrom(rawBytes, registry);
|
||||
|
||||
TestUtil.assertAllExtensionsSet(message3);
|
||||
}
|
||||
|
||||
public void testParsePackedExtensionsLite() throws Exception {
|
||||
// Ensure that packed extensions can be properly parsed.
|
||||
TestPackedExtensionsLite message = TestUtil.getLitePackedExtensionsSet();
|
||||
ByteString rawBytes = message.toByteString();
|
||||
|
||||
ExtensionRegistryLite registry = TestUtil.getExtensionRegistryLite();
|
||||
|
||||
TestPackedExtensionsLite message2 =
|
||||
TestPackedExtensionsLite.parseFrom(rawBytes, registry);
|
||||
|
||||
TestUtil.assertPackedExtensionsSet(message2);
|
||||
}
|
||||
|
||||
public void testExtensionsSerializedSize() throws Exception {
|
||||
assertEquals(TestUtil.getAllSet().getSerializedSize(),
|
||||
TestUtil.getAllExtensionsSet().getSerializedSize());
|
||||
}
|
||||
|
||||
public void testSerializeDelimited() throws Exception {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
TestUtil.getAllSet().writeDelimitedTo(output);
|
||||
output.write(12);
|
||||
TestUtil.getPackedSet().writeDelimitedTo(output);
|
||||
output.write(34);
|
||||
|
||||
ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
|
||||
|
||||
TestUtil.assertAllFieldsSet(TestAllTypes.parseDelimitedFrom(input));
|
||||
assertEquals(12, input.read());
|
||||
TestUtil.assertPackedFieldsSet(TestPackedTypes.parseDelimitedFrom(input));
|
||||
assertEquals(34, input.read());
|
||||
assertEquals(-1, input.read());
|
||||
|
||||
// We're at EOF, so parsing again should return null.
|
||||
assertTrue(TestAllTypes.parseDelimitedFrom(input) == null);
|
||||
}
|
||||
|
||||
private void assertFieldsInOrder(ByteString data) throws Exception {
|
||||
CodedInputStream input = data.newCodedInput();
|
||||
int previousTag = 0;
|
||||
|
||||
while (true) {
|
||||
int tag = input.readTag();
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
assertTrue(tag > previousTag);
|
||||
previousTag = tag;
|
||||
input.skipField(tag);
|
||||
}
|
||||
}
|
||||
|
||||
public void testInterleavedFieldsAndExtensions() throws Exception {
|
||||
// Tests that fields are written in order even when extension ranges
|
||||
// are interleaved with field numbers.
|
||||
ByteString data =
|
||||
TestFieldOrderings.newBuilder()
|
||||
.setMyInt(1)
|
||||
.setMyString("foo")
|
||||
.setMyFloat(1.0F)
|
||||
.setExtension(UnittestProto.myExtensionInt, 23)
|
||||
.setExtension(UnittestProto.myExtensionString, "bar")
|
||||
.build().toByteString();
|
||||
assertFieldsInOrder(data);
|
||||
|
||||
Descriptors.Descriptor descriptor = TestFieldOrderings.getDescriptor();
|
||||
ByteString dynamic_data =
|
||||
DynamicMessage.newBuilder(TestFieldOrderings.getDescriptor())
|
||||
.setField(descriptor.findFieldByName("my_int"), 1L)
|
||||
.setField(descriptor.findFieldByName("my_string"), "foo")
|
||||
.setField(descriptor.findFieldByName("my_float"), 1.0F)
|
||||
.setField(UnittestProto.myExtensionInt.getDescriptor(), 23)
|
||||
.setField(UnittestProto.myExtensionString.getDescriptor(), "bar")
|
||||
.build().toByteString();
|
||||
assertFieldsInOrder(dynamic_data);
|
||||
}
|
||||
|
||||
private ExtensionRegistry getTestFieldOrderingsRegistry() {
|
||||
ExtensionRegistry result = ExtensionRegistry.newInstance();
|
||||
result.add(UnittestProto.myExtensionInt);
|
||||
result.add(UnittestProto.myExtensionString);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testParseMultipleExtensionRanges() throws Exception {
|
||||
// Make sure we can parse a message that contains multiple extensions
|
||||
// ranges.
|
||||
TestFieldOrderings source =
|
||||
TestFieldOrderings.newBuilder()
|
||||
.setMyInt(1)
|
||||
.setMyString("foo")
|
||||
.setMyFloat(1.0F)
|
||||
.setExtension(UnittestProto.myExtensionInt, 23)
|
||||
.setExtension(UnittestProto.myExtensionString, "bar")
|
||||
.build();
|
||||
TestFieldOrderings dest =
|
||||
TestFieldOrderings.parseFrom(source.toByteString(),
|
||||
getTestFieldOrderingsRegistry());
|
||||
assertEquals(source, dest);
|
||||
}
|
||||
|
||||
public void testParseMultipleExtensionRangesDynamic() throws Exception {
|
||||
// Same as above except with DynamicMessage.
|
||||
Descriptors.Descriptor descriptor = TestFieldOrderings.getDescriptor();
|
||||
DynamicMessage source =
|
||||
DynamicMessage.newBuilder(TestFieldOrderings.getDescriptor())
|
||||
.setField(descriptor.findFieldByName("my_int"), 1L)
|
||||
.setField(descriptor.findFieldByName("my_string"), "foo")
|
||||
.setField(descriptor.findFieldByName("my_float"), 1.0F)
|
||||
.setField(UnittestProto.myExtensionInt.getDescriptor(), 23)
|
||||
.setField(UnittestProto.myExtensionString.getDescriptor(), "bar")
|
||||
.build();
|
||||
DynamicMessage dest =
|
||||
DynamicMessage.parseFrom(descriptor, source.toByteString(),
|
||||
getTestFieldOrderingsRegistry());
|
||||
assertEquals(source, dest);
|
||||
}
|
||||
|
||||
private static final int UNKNOWN_TYPE_ID = 1550055;
|
||||
private static final int TYPE_ID_1 =
|
||||
TestMessageSetExtension1.getDescriptor().getExtensions().get(0).getNumber();
|
||||
private static final int TYPE_ID_2 =
|
||||
TestMessageSetExtension2.getDescriptor().getExtensions().get(0).getNumber();
|
||||
|
||||
public void testSerializeMessageSetEagerly() throws Exception {
|
||||
testSerializeMessageSetWithFlag(true);
|
||||
}
|
||||
|
||||
public void testSerializeMessageSetNotEagerly() throws Exception {
|
||||
testSerializeMessageSetWithFlag(false);
|
||||
}
|
||||
|
||||
private void testSerializeMessageSetWithFlag(boolean eagerParsing)
|
||||
throws Exception {
|
||||
ExtensionRegistryLite.setEagerlyParseMessageSets(eagerParsing);
|
||||
// Set up a TestMessageSet with two known messages and an unknown one.
|
||||
TestMessageSet messageSet =
|
||||
TestMessageSet.newBuilder()
|
||||
.setExtension(
|
||||
TestMessageSetExtension1.messageSetExtension,
|
||||
TestMessageSetExtension1.newBuilder().setI(123).build())
|
||||
.setExtension(
|
||||
TestMessageSetExtension2.messageSetExtension,
|
||||
TestMessageSetExtension2.newBuilder().setStr("foo").build())
|
||||
.setUnknownFields(
|
||||
UnknownFieldSet.newBuilder()
|
||||
.addField(UNKNOWN_TYPE_ID,
|
||||
UnknownFieldSet.Field.newBuilder()
|
||||
.addLengthDelimited(ByteString.copyFromUtf8("bar"))
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ByteString data = messageSet.toByteString();
|
||||
|
||||
// Parse back using RawMessageSet and check the contents.
|
||||
RawMessageSet raw = RawMessageSet.parseFrom(data);
|
||||
|
||||
assertTrue(raw.getUnknownFields().asMap().isEmpty());
|
||||
|
||||
assertEquals(3, raw.getItemCount());
|
||||
assertEquals(TYPE_ID_1, raw.getItem(0).getTypeId());
|
||||
assertEquals(TYPE_ID_2, raw.getItem(1).getTypeId());
|
||||
assertEquals(UNKNOWN_TYPE_ID, raw.getItem(2).getTypeId());
|
||||
|
||||
TestMessageSetExtension1 message1 =
|
||||
TestMessageSetExtension1.parseFrom(
|
||||
raw.getItem(0).getMessage().toByteArray());
|
||||
assertEquals(123, message1.getI());
|
||||
|
||||
TestMessageSetExtension2 message2 =
|
||||
TestMessageSetExtension2.parseFrom(
|
||||
raw.getItem(1).getMessage().toByteArray());
|
||||
assertEquals("foo", message2.getStr());
|
||||
|
||||
assertEquals("bar", raw.getItem(2).getMessage().toStringUtf8());
|
||||
}
|
||||
|
||||
public void testParseMessageSetEagerly() throws Exception {
|
||||
testParseMessageSetWithFlag(true);
|
||||
}
|
||||
|
||||
public void testParseMessageSetNotEagerly()throws Exception {
|
||||
testParseMessageSetWithFlag(false);
|
||||
}
|
||||
|
||||
private void testParseMessageSetWithFlag(boolean eagerParsing)
|
||||
throws Exception {
|
||||
ExtensionRegistryLite.setEagerlyParseMessageSets(eagerParsing);
|
||||
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
|
||||
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
|
||||
extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
|
||||
|
||||
// Set up a RawMessageSet with two known messages and an unknown one.
|
||||
RawMessageSet raw =
|
||||
RawMessageSet.newBuilder()
|
||||
.addItem(
|
||||
RawMessageSet.Item.newBuilder()
|
||||
.setTypeId(TYPE_ID_1)
|
||||
.setMessage(
|
||||
TestMessageSetExtension1.newBuilder()
|
||||
.setI(123)
|
||||
.build().toByteString())
|
||||
.build())
|
||||
.addItem(
|
||||
RawMessageSet.Item.newBuilder()
|
||||
.setTypeId(TYPE_ID_2)
|
||||
.setMessage(
|
||||
TestMessageSetExtension2.newBuilder()
|
||||
.setStr("foo")
|
||||
.build().toByteString())
|
||||
.build())
|
||||
.addItem(
|
||||
RawMessageSet.Item.newBuilder()
|
||||
.setTypeId(UNKNOWN_TYPE_ID)
|
||||
.setMessage(ByteString.copyFromUtf8("bar"))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ByteString data = raw.toByteString();
|
||||
|
||||
// Parse as a TestMessageSet and check the contents.
|
||||
TestMessageSet messageSet =
|
||||
TestMessageSet.parseFrom(data, extensionRegistry);
|
||||
|
||||
assertEquals(123, messageSet.getExtension(
|
||||
TestMessageSetExtension1.messageSetExtension).getI());
|
||||
assertEquals("foo", messageSet.getExtension(
|
||||
TestMessageSetExtension2.messageSetExtension).getStr());
|
||||
|
||||
// Check for unknown field with type LENGTH_DELIMITED,
|
||||
// number UNKNOWN_TYPE_ID, and contents "bar".
|
||||
UnknownFieldSet unknownFields = messageSet.getUnknownFields();
|
||||
assertEquals(1, unknownFields.asMap().size());
|
||||
assertTrue(unknownFields.hasField(UNKNOWN_TYPE_ID));
|
||||
|
||||
UnknownFieldSet.Field field = unknownFields.getField(UNKNOWN_TYPE_ID);
|
||||
assertEquals(1, field.getLengthDelimitedList().size());
|
||||
assertEquals("bar", field.getLengthDelimitedList().get(0).toStringUtf8());
|
||||
}
|
||||
|
||||
public void testParseMessageSetExtensionEagerly() throws Exception {
|
||||
testParseMessageSetExtensionWithFlag(true);
|
||||
}
|
||||
|
||||
public void testParseMessageSetExtensionNotEagerly() throws Exception {
|
||||
testParseMessageSetExtensionWithFlag(false);
|
||||
}
|
||||
|
||||
private void testParseMessageSetExtensionWithFlag(boolean eagerParsing)
|
||||
throws Exception {
|
||||
ExtensionRegistryLite.setEagerlyParseMessageSets(eagerParsing);
|
||||
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
|
||||
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
|
||||
|
||||
// Set up a RawMessageSet with a known messages.
|
||||
int TYPE_ID_1 =
|
||||
TestMessageSetExtension1
|
||||
.getDescriptor().getExtensions().get(0).getNumber();
|
||||
RawMessageSet raw =
|
||||
RawMessageSet.newBuilder()
|
||||
.addItem(
|
||||
RawMessageSet.Item.newBuilder()
|
||||
.setTypeId(TYPE_ID_1)
|
||||
.setMessage(
|
||||
TestMessageSetExtension1.newBuilder()
|
||||
.setI(123)
|
||||
.build().toByteString())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ByteString data = raw.toByteString();
|
||||
|
||||
// Parse as a TestMessageSet and check the contents.
|
||||
TestMessageSet messageSet =
|
||||
TestMessageSet.parseFrom(data, extensionRegistry);
|
||||
assertEquals(123, messageSet.getExtension(
|
||||
TestMessageSetExtension1.messageSetExtension).getI());
|
||||
}
|
||||
|
||||
public void testMergeLazyMessageSetExtensionEagerly() throws Exception {
|
||||
testMergeLazyMessageSetExtensionWithFlag(true);
|
||||
}
|
||||
|
||||
public void testMergeLazyMessageSetExtensionNotEagerly() throws Exception {
|
||||
testMergeLazyMessageSetExtensionWithFlag(false);
|
||||
}
|
||||
|
||||
private void testMergeLazyMessageSetExtensionWithFlag(boolean eagerParsing)
|
||||
throws Exception {
|
||||
ExtensionRegistryLite.setEagerlyParseMessageSets(eagerParsing);
|
||||
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
|
||||
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
|
||||
|
||||
// Set up a RawMessageSet with a known messages.
|
||||
int TYPE_ID_1 =
|
||||
TestMessageSetExtension1
|
||||
.getDescriptor().getExtensions().get(0).getNumber();
|
||||
RawMessageSet raw =
|
||||
RawMessageSet.newBuilder()
|
||||
.addItem(
|
||||
RawMessageSet.Item.newBuilder()
|
||||
.setTypeId(TYPE_ID_1)
|
||||
.setMessage(
|
||||
TestMessageSetExtension1.newBuilder()
|
||||
.setI(123)
|
||||
.build().toByteString())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ByteString data = raw.toByteString();
|
||||
|
||||
// Parse as a TestMessageSet and store value into lazy field
|
||||
TestMessageSet messageSet =
|
||||
TestMessageSet.parseFrom(data, extensionRegistry);
|
||||
// Merge lazy field check the contents.
|
||||
messageSet =
|
||||
messageSet.toBuilder().mergeFrom(data, extensionRegistry).build();
|
||||
assertEquals(123, messageSet.getExtension(
|
||||
TestMessageSetExtension1.messageSetExtension).getI());
|
||||
}
|
||||
|
||||
public void testMergeMessageSetExtensionEagerly() throws Exception {
|
||||
testMergeMessageSetExtensionWithFlag(true);
|
||||
}
|
||||
|
||||
public void testMergeMessageSetExtensionNotEagerly() throws Exception {
|
||||
testMergeMessageSetExtensionWithFlag(false);
|
||||
}
|
||||
|
||||
private void testMergeMessageSetExtensionWithFlag(boolean eagerParsing)
|
||||
throws Exception {
|
||||
ExtensionRegistryLite.setEagerlyParseMessageSets(eagerParsing);
|
||||
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
|
||||
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
|
||||
|
||||
// Set up a RawMessageSet with a known messages.
|
||||
int TYPE_ID_1 =
|
||||
TestMessageSetExtension1
|
||||
.getDescriptor().getExtensions().get(0).getNumber();
|
||||
RawMessageSet raw =
|
||||
RawMessageSet.newBuilder()
|
||||
.addItem(
|
||||
RawMessageSet.Item.newBuilder()
|
||||
.setTypeId(TYPE_ID_1)
|
||||
.setMessage(
|
||||
TestMessageSetExtension1.newBuilder()
|
||||
.setI(123)
|
||||
.build().toByteString())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
// Serialize RawMessageSet unnormally (message value before type id)
|
||||
ByteString.CodedBuilder out = ByteString.newCodedBuilder(
|
||||
raw.getSerializedSize());
|
||||
CodedOutputStream output = out.getCodedOutput();
|
||||
List<RawMessageSet.Item> items = raw.getItemList();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
RawMessageSet.Item item = items.get(i);
|
||||
output.writeTag(1, WireFormat.WIRETYPE_START_GROUP);
|
||||
output.writeBytes(3, item.getMessage());
|
||||
output.writeInt32(2, item.getTypeId());
|
||||
output.writeTag(1, WireFormat.WIRETYPE_END_GROUP);
|
||||
}
|
||||
ByteString data = out.build();
|
||||
|
||||
// Merge bytes into TestMessageSet and check the contents.
|
||||
TestMessageSet messageSet =
|
||||
TestMessageSet.newBuilder().mergeFrom(data, extensionRegistry).build();
|
||||
assertEquals(123, messageSet.getExtension(
|
||||
TestMessageSetExtension1.messageSetExtension).getI());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: kenton@google.com (Kenton Varda)
|
||||
//
|
||||
// A proto file which tests the java_multiple_files option.
|
||||
|
||||
|
||||
// Some generic_services option(s) added automatically.
|
||||
// See: http://go/proto2-generic-services-default
|
||||
option java_generic_services = true; // auto-added
|
||||
|
||||
import "google/protobuf/unittest.proto";
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "MultipleFilesTestProto";
|
||||
|
||||
message MessageWithNoOuter {
|
||||
message NestedMessage {
|
||||
optional int32 i = 1;
|
||||
}
|
||||
enum NestedEnum {
|
||||
BAZ = 3;
|
||||
}
|
||||
optional NestedMessage nested = 1;
|
||||
repeated TestAllTypes foreign = 2;
|
||||
optional NestedEnum nested_enum = 3;
|
||||
optional EnumWithNoOuter foreign_enum = 4;
|
||||
}
|
||||
|
||||
enum EnumWithNoOuter {
|
||||
FOO = 1;
|
||||
BAR = 2;
|
||||
}
|
||||
|
||||
service ServiceWithNoOuter {
|
||||
rpc Foo(MessageWithNoOuter) returns(TestAllTypes);
|
||||
}
|
||||
|
||||
extend TestAllExtensions {
|
||||
optional int32 extension_with_outer = 1234567;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: jonp@google.com (Jon Perlow)
|
||||
//
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "NestedBuilders";
|
||||
|
||||
|
||||
message Vehicle {
|
||||
optional Engine engine = 1;
|
||||
repeated Wheel wheel = 2;
|
||||
}
|
||||
|
||||
message Engine {
|
||||
optional int32 cylinder = 1;
|
||||
optional int32 liters = 2;
|
||||
}
|
||||
|
||||
message Wheel {
|
||||
optional int32 radius = 1;
|
||||
optional int32 width = 2;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with nested extensions. Note that this must be defined in
|
||||
// a separate file to properly test the initialization of the outer class.
|
||||
|
||||
|
||||
import "com/google/protobuf/non_nested_extension.proto";
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
message MyNestedExtension {
|
||||
extend MessageToBeExtended {
|
||||
optional MessageToBeExtended recursiveExtension = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with nested extensions for a MessageLite messages. Note that
|
||||
// this must be defined in a separate file to properly test the initialization
|
||||
// of the outer class.
|
||||
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
import "com/google/protobuf/non_nested_extension_lite.proto";
|
||||
|
||||
message MyNestedExtensionLite {
|
||||
extend MessageLiteToBeExtended {
|
||||
optional MessageLiteToBeExtended recursiveExtensionLite = 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with extensions.
|
||||
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
message MessageToBeExtended {
|
||||
extensions 1 to max;
|
||||
}
|
||||
|
||||
message MyNonNestedExtension {
|
||||
}
|
||||
|
||||
extend MessageToBeExtended {
|
||||
optional MyNonNestedExtension nonNestedExtension = 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: Darick Tong (darick@google.com)
|
||||
//
|
||||
// A proto file with extensions for a MessageLite messages.
|
||||
|
||||
|
||||
package protobuf_unittest;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message MessageLiteToBeExtended {
|
||||
extensions 1 to max;
|
||||
}
|
||||
|
||||
message MyNonNestedExtensionLite {
|
||||
}
|
||||
|
||||
extend MessageLiteToBeExtended {
|
||||
optional MyNonNestedExtensionLite nonNestedExtensionLite = 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// http://code.google.com/p/protobuf/
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Author: jonp@google.com (Jon Perlow)
|
||||
|
||||
// This file tests that various identifiers work as field and type names even
|
||||
// though the same identifiers are used internally by the java code generator.
|
||||
|
||||
|
||||
// Some generic_services option(s) added automatically.
|
||||
// See: http://go/proto2-generic-services-default
|
||||
option java_generic_services = true; // auto-added
|
||||
|
||||
package io_protocol_tests;
|
||||
|
||||
option java_package = "com.google.protobuf";
|
||||
option java_outer_classname = "TestBadIdentifiersProto";
|
||||
|
||||
message TestMessage {
|
||||
}
|
||||
|
||||
message Descriptor {
|
||||
option no_standard_descriptor_accessor = true;
|
||||
optional string descriptor = 1;
|
||||
message NestedDescriptor {
|
||||
option no_standard_descriptor_accessor = true;
|
||||
optional string descriptor = 1;
|
||||
}
|
||||
optional NestedDescriptor nested_descriptor = 2;
|
||||
}
|
||||
|
||||
message Parser {
|
||||
enum ParserEnum {
|
||||
PARSER = 1;
|
||||
}
|
||||
optional ParserEnum parser = 1;
|
||||
}
|
||||
|
||||
message Deprecated {
|
||||
enum TestEnum {
|
||||
FOO = 1;
|
||||
}
|
||||
|
||||
optional int32 field1 = 1 [deprecated=true];
|
||||
optional TestEnum field2 = 2 [deprecated=true];
|
||||
optional TestMessage field3 = 3 [deprecated=true];
|
||||
}
|
||||
|
||||
message Override {
|
||||
optional int32 override = 1;
|
||||
}
|
||||
|
||||
message Object {
|
||||
optional int32 object = 1;
|
||||
optional string string_object = 2;
|
||||
}
|
||||
|
||||
message String {
|
||||
optional string string = 1;
|
||||
}
|
||||
|
||||
message Integer {
|
||||
optional int32 integer = 1;
|
||||
}
|
||||
|
||||
message Long {
|
||||
optional int32 long = 1;
|
||||
}
|
||||
|
||||
message Float {
|
||||
optional float float = 1;
|
||||
}
|
||||
|
||||
message Double {
|
||||
optional double double = 1;
|
||||
}
|
||||
|
||||
service TestConflictingMethodNames {
|
||||
rpc Override(TestMessage) returns (TestMessage);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user