0,0 → 1,764 |
// 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.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; |
} |
|
@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; |
} |
|
/** |
* 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 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, |
this, tag)) { |
// end group tag |
break; |
} |
} |
setUnknownFields(unknownFields.build()); |
return (BuilderType) this; |
} |
|
/** |
* Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder, |
* ExtensionRegistryLite, Message.Builder)}, but parses a single field. |
* 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( |
final CodedInputStream input, |
final UnknownFieldSet.Builder unknownFields, |
final ExtensionRegistryLite extensionRegistry, |
final Message.Builder builder, |
final int tag) throws IOException { |
final Descriptor type = builder.getDescriptorForType(); |
|
if (type.getOptions().getMessageSetWireFormat() && |
tag == WireFormat.MESSAGE_SET_ITEM_TAG) { |
mergeMessageSetExtensionFromCodedStream( |
input, unknownFields, extensionRegistry, builder); |
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 { |
field = type.findFieldByNumber(fieldNumber); |
} |
|
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; |
} |
builder.addRepeatedField(field, value); |
} |
} else { |
while (input.getBytesUntilLimit() > 0) { |
final Object value = |
FieldSet.readPrimitiveField(input, field.getLiteType()); |
builder.addRepeatedField(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()) { |
subBuilder.mergeFrom((Message) builder.getField(field)); |
} |
input.readGroup(field.getNumber(), subBuilder, extensionRegistry); |
value = subBuilder.build(); |
break; |
} |
case MESSAGE: { |
final Message.Builder subBuilder; |
if (defaultInstance != null) { |
subBuilder = defaultInstance.newBuilderForType(); |
} else { |
subBuilder = builder.newBuilderForField(field); |
} |
if (!field.isRepeated()) { |
subBuilder.mergeFrom((Message) builder.getField(field)); |
} |
input.readMessage(subBuilder, extensionRegistry); |
value = subBuilder.build(); |
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()) { |
builder.addRepeatedField(field, value); |
} else { |
builder.setField(field, value); |
} |
} |
|
return true; |
} |
|
/** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */ |
private static void mergeMessageSetExtensionFromCodedStream( |
final CodedInputStream input, |
final UnknownFieldSet.Builder unknownFields, |
final ExtensionRegistryLite extensionRegistry, |
final Message.Builder builder) throws IOException { |
final Descriptor type = builder.getDescriptorForType(); |
|
// 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" |
Message.Builder subBuilder = null; |
FieldDescriptor field = null; |
|
while (true) { |
final int tag = input.readTag(); |
if (tag == 0) { |
break; |
} |
|
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { |
typeId = input.readUInt32(); |
// Zero is not a valid type ID. |
if (typeId != 0) { |
final ExtensionRegistry.ExtensionInfo extension; |
|
// 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 { |
extension = null; |
} |
|
if (extension != null) { |
field = extension.descriptor; |
subBuilder = extension.defaultInstance.newBuilderForType(); |
final Message originalMessage = (Message)builder.getField(field); |
if (originalMessage != null) { |
subBuilder.mergeFrom(originalMessage); |
} |
if (rawBytes != null) { |
// We already encountered the message. Parse it now. |
subBuilder.mergeFrom( |
CodedInputStream.newInstance(rawBytes.newInput())); |
rawBytes = null; |
} |
} else { |
// Unknown extension number. If we already saw data, put it |
// in rawBytes. |
if (rawBytes != null) { |
unknownFields.mergeField(typeId, |
UnknownFieldSet.Field.newBuilder() |
.addLengthDelimited(rawBytes) |
.build()); |
rawBytes = null; |
} |
} |
} |
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { |
if (typeId == 0) { |
// We haven't seen a type ID yet, so we have to store the raw bytes |
// for now. |
rawBytes = input.readBytes(); |
} else if (subBuilder == null) { |
// We don't know how to parse this. Ignore it. |
unknownFields.mergeField(typeId, |
UnknownFieldSet.Field.newBuilder() |
.addLengthDelimited(input.readBytes()) |
.build()); |
} else { |
// We already know the type, so we can parse directly from the input |
// with no copying. Hooray! |
input.readMessage(subBuilder, extensionRegistry); |
} |
} else { |
// Unknown tag. Skip it. |
if (!input.skipField(tag)) { |
break; // end of group |
} |
} |
} |
|
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); |
|
if (subBuilder != null) { |
builder.setField(field, subBuilder.build()); |
} |
} |
|
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { |
setUnknownFields( |
UnknownFieldSet.newBuilder(getUnknownFields()) |
.mergeFrom(unknownFields) |
.build()); |
return (BuilderType) this; |
} |
|
/** |
* 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 Message message) { |
final List<String> results = new ArrayList<String>(); |
findMissingFields(message, "", results); |
return results; |
} |
|
/** Recursive helper implementing {@link #findMissingFields(Message)}. */ |
private static void findMissingFields(final Message 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((Message) element, |
subMessagePrefix(prefix, field, i++), |
results); |
} |
} else { |
if (message.hasField(field)) { |
findMissingFields((Message) 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); |
} |
|
} |
} |