- created new subproject akka-protobuf (and added COPYING and LICENSE) - renamed com.google.protobuf -> akka.protobuf everywhere - also added such renaming step to the results of protoc compilation in project/Protobuf.scala - had to include transcriptions of Netty’s ProtobufEncoder/Decoder to make multi-node-testkit compile again
930 lines
32 KiB
Java
930 lines
32 KiB
Java
// 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 akka.protobuf;
|
|
|
|
import akka.protobuf.Descriptors.Descriptor;
|
|
import akka.protobuf.Descriptors.FieldDescriptor;
|
|
import akka.protobuf.GeneratedMessage.ExtendableBuilder;
|
|
import akka.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
|
|
// akka.protobuf.AbstractMessage.Builder<
|
|
// akka.protobuf.DynamicMessage.Builder> and
|
|
// akka.protobuf.AbstractMessage.Builder<
|
|
// akka.protobuf.DynamicMessage.Builder> are incompatible; both
|
|
// define mergeFrom(akka.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);
|
|
}
|
|
|
|
}
|
|
}
|