// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // 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.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.OneofDescriptor; import java.io.InputStream; import java.io.IOException; import java.util.Collections; import java.util.List; 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 fields; private final FieldDescriptor[] oneofCases; private final UnknownFieldSet unknownFields; private int memoizedSize = -1; /** * Construct a {@code DynamicMessage} using the given {@code FieldSet}. * oneofCases stores the FieldDescriptor for each oneof to indicate * which field is set. Caller should make sure the array is immutable. * * This contructor is package private and will be used in * {@code DynamicMutableMessage} to convert a mutable message to an immutable * message. */ DynamicMessage(Descriptor type, FieldSet fields, FieldDescriptor[] oneofCases, UnknownFieldSet unknownFields) { this.type = type; this.fields = fields; this.oneofCases = oneofCases; this.unknownFields = unknownFields; } /** * Get a {@code DynamicMessage} representing the default instance of the * given type. */ public static DynamicMessage getDefaultInstance(Descriptor type) { int oneofDeclCount = type.toProto().getOneofDeclCount(); FieldDescriptor[] oneofCases = new FieldDescriptor[oneofDeclCount]; return new DynamicMessage(type, FieldSet.emptySet(), oneofCases, 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 getAllFields() { return fields.getAllFields(); } public boolean hasOneof(OneofDescriptor oneof) { verifyOneofContainingType(oneof); FieldDescriptor field = oneofCases[oneof.getIndex()]; if (field == null) { return false; } return true; } public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { verifyOneofContainingType(oneof); return oneofCases[oneof.getIndex()]; } 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; } static boolean isInitialized(Descriptor type, FieldSet 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 getParserForType() { return new AbstractParser() { 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."); } } /** Verifies that the oneof is an oneof of this message. */ private void verifyOneofContainingType(OneofDescriptor oneof) { if (oneof.getContainingType() != type) { throw new IllegalArgumentException( "OneofDescriptor does not match message type."); } } // ================================================================= /** * Builder for {@link DynamicMessage}s. */ public static final class Builder extends AbstractMessage.Builder { private final Descriptor type; private FieldSet fields; private final FieldDescriptor[] oneofCases; 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(); this.oneofCases = new FieldDescriptor[type.toProto().getOneofDeclCount()]; } // --------------------------------------------------------------- // 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); for (int i = 0; i < oneofCases.length; i++) { if (oneofCases[i] == null) { oneofCases[i] = otherDynamicMessage.oneofCases[i]; } else { if ((otherDynamicMessage.oneofCases[i] != null) && (oneofCases[i] != otherDynamicMessage.oneofCases[i])) { fields.clearField(oneofCases[i]); oneofCases[i] = otherDynamicMessage.oneofCases[i]; } } } return this; } else { return super.mergeFrom(other); } } public DynamicMessage build() { if (!isInitialized()) { throw newUninitializedMessageException( new DynamicMessage(type, fields, java.util.Arrays.copyOf(oneofCases, oneofCases.length), 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, java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields)) .asInvalidProtocolBufferException(); } return buildPartial(); } public DynamicMessage buildPartial() { fields.makeImmutable(); DynamicMessage result = new DynamicMessage(type, fields, java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields); return result; } @Override public Builder clone() { Builder result = new Builder(type); result.fields.mergeFrom(fields); result.mergeUnknownFields(unknownFields); System.arraycopy(oneofCases, 0, result.oneofCases, 0 , oneofCases.length); return result; } public boolean isInitialized() { return DynamicMessage.isInitialized(type, fields); } public Descriptor getDescriptorForType() { return type; } public DynamicMessage getDefaultInstanceForType() { return getDefaultInstance(type); } public Map 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 hasOneof(OneofDescriptor oneof) { verifyOneofContainingType(oneof); FieldDescriptor field = oneofCases[oneof.getIndex()]; if (field == null) { return false; } return true; } public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { verifyOneofContainingType(oneof); return oneofCases[oneof.getIndex()]; } public Builder clearOneof(OneofDescriptor oneof) { verifyOneofContainingType(oneof); FieldDescriptor field = oneofCases[oneof.getIndex()]; if (field != null) { clearField(field); } return this; } 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 Builder setField(FieldDescriptor field, Object value) { verifyContainingType(field); ensureIsMutable(); // TODO(xiaofeng): This check should really be put in FieldSet.setField() // where all other such checks are done. However, currently // FieldSet.setField() permits Integer value for enum fields probably // because of some internal features we support. Should figure it out // and move this check to a more appropriate place. if (field.getType() == FieldDescriptor.Type.ENUM) { ensureEnumValueDescriptor(field, value); } OneofDescriptor oneofDescriptor = field.getContainingOneof(); if (oneofDescriptor != null) { int index = oneofDescriptor.getIndex(); FieldDescriptor oldField = oneofCases[index]; if ((oldField != null) && (oldField != field)) { fields.clearField(oldField); } oneofCases[index] = field; } fields.setField(field, value); return this; } public Builder clearField(FieldDescriptor field) { verifyContainingType(field); ensureIsMutable(); OneofDescriptor oneofDescriptor = field.getContainingOneof(); if (oneofDescriptor != null) { int index = oneofDescriptor.getIndex(); if (oneofCases[index] == field) { oneofCases[index] = null; } } 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."); } } /** Verifies that the oneof is an oneof of this message. */ private void verifyOneofContainingType(OneofDescriptor oneof) { if (oneof.getContainingType() != type) { throw new IllegalArgumentException( "OneofDescriptor does not match message type."); } } /** Verifies that the value is EnumValueDescriptor and matches Enum Type. */ private void ensureSingularEnumValueDescriptor( FieldDescriptor field, Object value) { if (value == null) { throw new NullPointerException(); } if (!(value instanceof EnumValueDescriptor)) { throw new IllegalArgumentException( "DynamicMessage should use EnumValueDescriptor to set Enum Value."); } if (field.getEnumType() != ((EnumValueDescriptor) value).getType()) { throw new IllegalArgumentException( "EnumValueDescriptor doesn't much Enum Field."); } } /** Verifies the value for an enum field. */ private void ensureEnumValueDescriptor( FieldDescriptor field, Object value) { if (field.isRepeated()) { for (Object item : (List) value) { ensureSingularEnumValueDescriptor(field, item); } } else { ensureSingularEnumValueDescriptor(field, value); } } 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."); } } }