Squashed 'src/cpp/protobuf/' content from commit 29edac6

git-subtree-dir: src/cpp/protobuf
git-subtree-split: 29edac6e7d78bdb050ed47adc79c9ac1ba22071e
This commit is contained in:
Vinnie Falco
2013-05-12 12:16:22 -07:00
commit e2ca3615b3
407 changed files with 170173 additions and 0 deletions

96
java/README.txt Normal file
View 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
View 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>

View 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);
}
}
}

View 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();
}
}
}
}
}

View 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);
}
}

View File

@@ -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;
}

View 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);
}

View 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();
}
}
}

View 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());
}
}

View 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;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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.");
}
}
}

View 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;
}
}
}

View 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 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;
}
}
}

View 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);
}
}
}

File diff suppressed because it is too large Load Diff

View 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);
}
}

View 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);
}
}

View File

@@ -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.");
}
}

View 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();
}
}
}

View 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);
}
}

View 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();
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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();
}

View 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();
}

View 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;
}

View File

@@ -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();
}

View 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++;
}
}
}

View 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;
}
}
}
}
}

View 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);
}

View 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);
}

View 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);
}

View 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.");
}
}
}

View 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);
}

View 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);
}
}

View 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();
}
}

View 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;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}
}

View 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;
}
}

View File

@@ -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();
}
}

View 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();
}
}
}

View 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);
}

View 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());
}
}

View File

@@ -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);
}
}

View 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;
}
}

View 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());
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View 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"));
}
}
}

View 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());
}
}

View File

@@ -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();
}
}

File diff suppressed because it is too large Load Diff

View 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);
}
}

View 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();
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View 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());
}
}

View File

@@ -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);
}
}

View 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);
}
}

View 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());
}
}

View 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));
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View 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());
}
}

View 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)");
}
}
}

View File

@@ -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());
}
}

View 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));
}
}

View File

@@ -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();
}
}

File diff suppressed because it is too large Load Diff

View 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()));
}
}

View 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());
}
}

View File

@@ -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;
}
}

View 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());
}
}

View 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.
// 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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}