diff options
Diffstat (limited to 'java/src/main/java/com/google/protobuf/DynamicMessage.java')
-rw-r--r-- | java/src/main/java/com/google/protobuf/DynamicMessage.java | 226 |
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."); + } } } |