summaryrefslogtreecommitdiffstats
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.java226
1 files changed, 205 insertions, 21 deletions
diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java
index c106b66..c9ce667 100644
--- a/java/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -31,10 +31,14 @@
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;
/**
@@ -46,16 +50,25 @@ import java.util.Map;
public final class DynamicMessage extends AbstractMessage {
private final Descriptor type;
private final FieldSet<FieldDescriptor> 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.
*/
- private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
- UnknownFieldSet unknownFields) {
+ DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
+ FieldDescriptor[] oneofCases,
+ UnknownFieldSet unknownFields) {
this.type = type;
this.fields = fields;
+ this.oneofCases = oneofCases;
this.unknownFields = unknownFields;
}
@@ -64,10 +77,14 @@ public final class DynamicMessage extends AbstractMessage {
* given type.
*/
public static DynamicMessage getDefaultInstance(Descriptor type) {
+ int oneofDeclCount = type.toProto().getOneofDeclCount();
+ FieldDescriptor[] oneofCases = new FieldDescriptor[oneofDeclCount];
return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
+ oneofCases,
UnknownFieldSet.getDefaultInstance());
}
+
/** Parse a message of the given type from the given input stream. */
public static DynamicMessage parseFrom(Descriptor type,
CodedInputStream input)
@@ -151,6 +168,20 @@ public final class DynamicMessage extends AbstractMessage {
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);
@@ -160,7 +191,9 @@ public final class DynamicMessage extends AbstractMessage {
verifyContainingType(field);
Object result = fields.getField(field);
if (result == null) {
- if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (field.isRepeated()) {
+ result = Collections.emptyList();
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
result = getDefaultInstance(field.getMessageType());
} else {
result = field.getDefaultValue();
@@ -183,8 +216,8 @@ public final class DynamicMessage extends AbstractMessage {
return unknownFields;
}
- private static boolean isInitialized(Descriptor type,
- FieldSet<FieldDescriptor> fields) {
+ static boolean isInitialized(Descriptor type,
+ FieldSet<FieldDescriptor> fields) {
// Check that all required fields are present.
for (final FieldDescriptor field : type.getFields()) {
if (field.isRequired()) {
@@ -198,10 +231,12 @@ public final class DynamicMessage extends AbstractMessage {
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);
@@ -212,6 +247,7 @@ public final class DynamicMessage extends AbstractMessage {
}
}
+ @Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
@@ -236,6 +272,26 @@ public final class DynamicMessage extends AbstractMessage {
return newBuilderForType().mergeFrom(this);
}
+ public Parser<DynamicMessage> getParserForType() {
+ return new AbstractParser<DynamicMessage>() {
+ 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) {
@@ -244,6 +300,14 @@ public final class DynamicMessage extends AbstractMessage {
}
}
+ /** 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.");
+ }
+ }
+
// =================================================================
/**
@@ -252,6 +316,7 @@ public final class DynamicMessage extends AbstractMessage {
public static final class Builder extends AbstractMessage.Builder<Builder> {
private final Descriptor type;
private FieldSet<FieldDescriptor> fields;
+ private final FieldDescriptor[] oneofCases;
private UnknownFieldSet unknownFields;
/** Construct a {@code Builder} for the given type. */
@@ -259,19 +324,24 @@ public final class DynamicMessage extends AbstractMessage {
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 == null) {
- throw new IllegalStateException("Cannot call clear() after build().");
+ if (fields.isImmutable()) {
+ fields = FieldSet.newFieldSet();
+ } else {
+ fields.clear();
}
- 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().
@@ -280,8 +350,20 @@ public final class DynamicMessage extends AbstractMessage {
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);
@@ -289,10 +371,10 @@ public final class DynamicMessage extends AbstractMessage {
}
public DynamicMessage build() {
- // If fields == null, we'll throw an appropriate exception later.
- if (fields != null && !isInitialized()) {
+ if (!isInitialized()) {
throw newUninitializedMessageException(
- new DynamicMessage(type, fields, unknownFields));
+ new DynamicMessage(type, fields,
+ java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields));
}
return buildPartial();
}
@@ -305,28 +387,27 @@ public final class DynamicMessage extends AbstractMessage {
private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
if (!isInitialized()) {
throw newUninitializedMessageException(
- new DynamicMessage(type, fields, unknownFields))
+ new DynamicMessage(type, fields,
+ java.util.Arrays.copyOf(oneofCases, oneofCases.length), 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;
+ 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;
}
@@ -357,6 +438,29 @@ public final class DynamicMessage extends AbstractMessage {
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);
@@ -366,7 +470,9 @@ public final class DynamicMessage extends AbstractMessage {
verifyContainingType(field);
Object result = fields.getField(field);
if (result == null) {
- if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (field.isRepeated()) {
+ result = Collections.emptyList();
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
result = getDefaultInstance(field.getMessageType());
} else {
result = field.getDefaultValue();
@@ -377,12 +483,38 @@ public final class DynamicMessage extends AbstractMessage {
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;
}
@@ -400,12 +532,14 @@ public final class DynamicMessage extends AbstractMessage {
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;
}
@@ -419,6 +553,7 @@ public final class DynamicMessage extends AbstractMessage {
return this;
}
+ @Override
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
this.unknownFields =
UnknownFieldSet.newBuilder(this.unknownFields)
@@ -434,5 +569,54 @@ public final class DynamicMessage extends AbstractMessage {
"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.");
+ }
}
}