aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/main/java/com/google/protobuf/DynamicMessage.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/main/java/com/google/protobuf/DynamicMessage.java')
-rw-r--r--java/src/main/java/com/google/protobuf/DynamicMessage.java438
1 files changed, 438 insertions, 0 deletions
diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java
new file mode 100644
index 0000000..c106b66
--- /dev/null
+++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -0,0 +1,438 @@
+// 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.");
+ }
+ }
+ }
+}