// 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.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.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(); } public boolean isInitialized() { return isInitialized(type, fields); } public void writeTo(CodedOutputStream output) throws IOException { if (type.getOptions().getMessageSetWireFormat()) { fields.writeMessageSetTo(output); unknownFields.writeAsMessageSetTo(output); } else { fields.writeTo(output); unknownFields.writeTo(output); } } 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); } /** 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. public Builder clear() { if (fields == null) { throw new IllegalStateException("Cannot call clear() after build()."); } fields.clear(); return this; } 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."); } fields.mergeFrom(otherDynamicMessage.fields); mergeUnknownFields(otherDynamicMessage.unknownFields); return this; } else { return super.mergeFrom(other); } } public DynamicMessage build() { // If fields == null, we'll throw an appropriate exception later. if (fields != null && !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() { if (fields == null) { throw new IllegalStateException( "build() has already been called on this Builder."); } fields.makeImmutable(); DynamicMessage result = new DynamicMessage(type, fields, unknownFields); fields = null; unknownFields = null; return result; } public Builder clone() { Builder result = new Builder(type); result.fields.mergeFrom(fields); 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); fields.setField(field, value); return this; } public Builder clearField(FieldDescriptor field) { verifyContainingType(field); 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); fields.setRepeatedField(field, index, value); return this; } public Builder addRepeatedField(FieldDescriptor field, Object value) { verifyContainingType(field); fields.addRepeatedField(field, value); return this; } public UnknownFieldSet getUnknownFields() { return unknownFields; } public Builder setUnknownFields(UnknownFieldSet unknownFields) { this.unknownFields = unknownFields; return this; } 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."); } } } }