• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import static com.google.protobuf.Internal.checkNotNull;
34 
35 import com.google.protobuf.Descriptors.Descriptor;
36 import com.google.protobuf.Descriptors.EnumValueDescriptor;
37 import com.google.protobuf.Descriptors.FieldDescriptor;
38 import com.google.protobuf.Descriptors.OneofDescriptor;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Map;
44 
45 /**
46  * An implementation of {@link Message} that can represent arbitrary types, given a {@link
47  * Descriptors.Descriptor}.
48  *
49  * @author kenton@google.com Kenton Varda
50  */
51 public final class DynamicMessage extends AbstractMessage {
52   private final Descriptor type;
53   private final FieldSet<FieldDescriptor> fields;
54   private final FieldDescriptor[] oneofCases;
55   private final UnknownFieldSet unknownFields;
56   private int memoizedSize = -1;
57 
58   /**
59    * Construct a {@code DynamicMessage} using the given {@code FieldSet}. oneofCases stores the
60    * FieldDescriptor for each oneof to indicate which field is set. Caller should make sure the
61    * array is immutable.
62    *
63    * <p>This constructor is package private and will be used in {@code DynamicMutableMessage} to
64    * convert a mutable message to an immutable message.
65    */
DynamicMessage( Descriptor type, FieldSet<FieldDescriptor> fields, FieldDescriptor[] oneofCases, UnknownFieldSet unknownFields)66   DynamicMessage(
67       Descriptor type,
68       FieldSet<FieldDescriptor> fields,
69       FieldDescriptor[] oneofCases,
70       UnknownFieldSet unknownFields) {
71     this.type = type;
72     this.fields = fields;
73     this.oneofCases = oneofCases;
74     this.unknownFields = unknownFields;
75   }
76 
77   /** Get a {@code DynamicMessage} representing the default instance of the given type. */
getDefaultInstance(Descriptor type)78   public static DynamicMessage getDefaultInstance(Descriptor type) {
79     int oneofDeclCount = type.toProto().getOneofDeclCount();
80     FieldDescriptor[] oneofCases = new FieldDescriptor[oneofDeclCount];
81     return new DynamicMessage(
82         type,
83         FieldSet.<FieldDescriptor>emptySet(),
84         oneofCases,
85         UnknownFieldSet.getDefaultInstance());
86   }
87 
88 
89   /** Parse a message of the given type from the given input stream. */
parseFrom(Descriptor type, CodedInputStream input)90   public static DynamicMessage parseFrom(Descriptor type, CodedInputStream input)
91       throws IOException {
92     return newBuilder(type).mergeFrom(input).buildParsed();
93   }
94 
95   /** Parse a message of the given type from the given input stream. */
parseFrom( Descriptor type, CodedInputStream input, ExtensionRegistry extensionRegistry)96   public static DynamicMessage parseFrom(
97       Descriptor type, CodedInputStream input, ExtensionRegistry extensionRegistry)
98       throws IOException {
99     return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
100   }
101 
102   /** Parse {@code data} as a message of the given type and return it. */
parseFrom(Descriptor type, ByteString data)103   public static DynamicMessage parseFrom(Descriptor type, ByteString data)
104       throws InvalidProtocolBufferException {
105     return newBuilder(type).mergeFrom(data).buildParsed();
106   }
107 
108   /** Parse {@code data} as a message of the given type and return it. */
parseFrom( Descriptor type, ByteString data, ExtensionRegistry extensionRegistry)109   public static DynamicMessage parseFrom(
110       Descriptor type, ByteString data, ExtensionRegistry extensionRegistry)
111       throws InvalidProtocolBufferException {
112     return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
113   }
114 
115   /** Parse {@code data} as a message of the given type and return it. */
parseFrom(Descriptor type, byte[] data)116   public static DynamicMessage parseFrom(Descriptor type, byte[] data)
117       throws InvalidProtocolBufferException {
118     return newBuilder(type).mergeFrom(data).buildParsed();
119   }
120 
121   /** Parse {@code data} as a message of the given type and return it. */
parseFrom( Descriptor type, byte[] data, ExtensionRegistry extensionRegistry)122   public static DynamicMessage parseFrom(
123       Descriptor type, byte[] data, ExtensionRegistry extensionRegistry)
124       throws InvalidProtocolBufferException {
125     return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
126   }
127 
128   /** Parse a message of the given type from {@code input} and return it. */
parseFrom(Descriptor type, InputStream input)129   public static DynamicMessage parseFrom(Descriptor type, InputStream input) throws IOException {
130     return newBuilder(type).mergeFrom(input).buildParsed();
131   }
132 
133   /** Parse a message of the given type from {@code input} and return it. */
parseFrom( Descriptor type, InputStream input, ExtensionRegistry extensionRegistry)134   public static DynamicMessage parseFrom(
135       Descriptor type, InputStream input, ExtensionRegistry extensionRegistry) throws IOException {
136     return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
137   }
138 
139   /** Construct a {@link Message.Builder} for the given type. */
newBuilder(Descriptor type)140   public static Builder newBuilder(Descriptor type) {
141     return new Builder(type);
142   }
143 
144   /**
145    * Construct a {@link Message.Builder} for a message of the same type as {@code prototype}, and
146    * initialize it with {@code prototype}'s contents.
147    */
newBuilder(Message prototype)148   public static Builder newBuilder(Message prototype) {
149     return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
150   }
151 
152   // -----------------------------------------------------------------
153   // Implementation of Message interface.
154 
155   @Override
getDescriptorForType()156   public Descriptor getDescriptorForType() {
157     return type;
158   }
159 
160   @Override
getDefaultInstanceForType()161   public DynamicMessage getDefaultInstanceForType() {
162     return getDefaultInstance(type);
163   }
164 
165   @Override
getAllFields()166   public Map<FieldDescriptor, Object> getAllFields() {
167     return fields.getAllFields();
168   }
169 
170   @Override
hasOneof(OneofDescriptor oneof)171   public boolean hasOneof(OneofDescriptor oneof) {
172     verifyOneofContainingType(oneof);
173     FieldDescriptor field = oneofCases[oneof.getIndex()];
174     if (field == null) {
175       return false;
176     }
177     return true;
178   }
179 
180   @Override
getOneofFieldDescriptor(OneofDescriptor oneof)181   public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
182     verifyOneofContainingType(oneof);
183     return oneofCases[oneof.getIndex()];
184   }
185 
186   @Override
hasField(FieldDescriptor field)187   public boolean hasField(FieldDescriptor field) {
188     verifyContainingType(field);
189     return fields.hasField(field);
190   }
191 
192   @Override
getField(FieldDescriptor field)193   public Object getField(FieldDescriptor field) {
194     verifyContainingType(field);
195     Object result = fields.getField(field);
196     if (result == null) {
197       if (field.isRepeated()) {
198         result = Collections.emptyList();
199       } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
200         result = getDefaultInstance(field.getMessageType());
201       } else {
202         result = field.getDefaultValue();
203       }
204     }
205     return result;
206   }
207 
208   @Override
getRepeatedFieldCount(FieldDescriptor field)209   public int getRepeatedFieldCount(FieldDescriptor field) {
210     verifyContainingType(field);
211     return fields.getRepeatedFieldCount(field);
212   }
213 
214   @Override
getRepeatedField(FieldDescriptor field, int index)215   public Object getRepeatedField(FieldDescriptor field, int index) {
216     verifyContainingType(field);
217     return fields.getRepeatedField(field, index);
218   }
219 
220   @Override
getUnknownFields()221   public UnknownFieldSet getUnknownFields() {
222     return unknownFields;
223   }
224 
isInitialized(Descriptor type, FieldSet<FieldDescriptor> fields)225   static boolean isInitialized(Descriptor type, FieldSet<FieldDescriptor> fields) {
226     // Check that all required fields are present.
227     for (final FieldDescriptor field : type.getFields()) {
228       if (field.isRequired()) {
229         if (!fields.hasField(field)) {
230           return false;
231         }
232       }
233     }
234 
235     // Check that embedded messages are initialized.
236     return fields.isInitialized();
237   }
238 
239   @Override
isInitialized()240   public boolean isInitialized() {
241     return isInitialized(type, fields);
242   }
243 
244   @Override
writeTo(CodedOutputStream output)245   public void writeTo(CodedOutputStream output) throws IOException {
246     if (type.getOptions().getMessageSetWireFormat()) {
247       fields.writeMessageSetTo(output);
248       unknownFields.writeAsMessageSetTo(output);
249     } else {
250       fields.writeTo(output);
251       unknownFields.writeTo(output);
252     }
253   }
254 
255   @Override
getSerializedSize()256   public int getSerializedSize() {
257     int size = memoizedSize;
258     if (size != -1) return size;
259 
260     if (type.getOptions().getMessageSetWireFormat()) {
261       size = fields.getMessageSetSerializedSize();
262       size += unknownFields.getSerializedSizeAsMessageSet();
263     } else {
264       size = fields.getSerializedSize();
265       size += unknownFields.getSerializedSize();
266     }
267 
268     memoizedSize = size;
269     return size;
270   }
271 
272   @Override
newBuilderForType()273   public Builder newBuilderForType() {
274     return new Builder(type);
275   }
276 
277   @Override
toBuilder()278   public Builder toBuilder() {
279     return newBuilderForType().mergeFrom(this);
280   }
281 
282   @Override
getParserForType()283   public Parser<DynamicMessage> getParserForType() {
284     return new AbstractParser<DynamicMessage>() {
285       @Override
286       public DynamicMessage parsePartialFrom(
287           CodedInputStream input, ExtensionRegistryLite extensionRegistry)
288           throws InvalidProtocolBufferException {
289         Builder builder = newBuilder(type);
290         try {
291           builder.mergeFrom(input, extensionRegistry);
292         } catch (InvalidProtocolBufferException e) {
293           throw e.setUnfinishedMessage(builder.buildPartial());
294         } catch (IOException e) {
295           throw new InvalidProtocolBufferException(e).setUnfinishedMessage(builder.buildPartial());
296         }
297         return builder.buildPartial();
298       }
299     };
300   }
301 
302   /** Verifies that the field is a field of this message. */
303   private void verifyContainingType(FieldDescriptor field) {
304     if (field.getContainingType() != type) {
305       throw new IllegalArgumentException("FieldDescriptor does not match message type.");
306     }
307   }
308 
309   /** Verifies that the oneof is an oneof of this message. */
310   private void verifyOneofContainingType(OneofDescriptor oneof) {
311     if (oneof.getContainingType() != type) {
312       throw new IllegalArgumentException("OneofDescriptor does not match message type.");
313     }
314   }
315 
316   // =================================================================
317 
318   /** Builder for {@link DynamicMessage}s. */
319   public static final class Builder extends AbstractMessage.Builder<Builder> {
320     private final Descriptor type;
321     private FieldSet<FieldDescriptor> fields;
322     private final FieldDescriptor[] oneofCases;
323     private UnknownFieldSet unknownFields;
324 
325     /** Construct a {@code Builder} for the given type. */
326     private Builder(Descriptor type) {
327       this.type = type;
328       this.fields = FieldSet.newFieldSet();
329       this.unknownFields = UnknownFieldSet.getDefaultInstance();
330       this.oneofCases = new FieldDescriptor[type.toProto().getOneofDeclCount()];
331       // A MapEntry has all of its fields present at all times.
332       if (type.getOptions().getMapEntry()) {
333         populateMapEntry();
334       }
335     }
336 
337     private void populateMapEntry() {
338       for (FieldDescriptor field : type.getFields()) {
339         if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
340           fields.setField(field, getDefaultInstance(field.getMessageType()));
341         } else {
342           fields.setField(field, field.getDefaultValue());
343         }
344       }
345     }
346 
347     // ---------------------------------------------------------------
348     // Implementation of Message.Builder interface.
349 
350     @Override
351     public Builder clear() {
352       if (fields.isImmutable()) {
353         fields = FieldSet.newFieldSet();
354       } else {
355         fields.clear();
356       }
357       // A MapEntry has all of its fields present at all times.
358       if (type.getOptions().getMapEntry()) {
359         populateMapEntry();
360       }
361       unknownFields = UnknownFieldSet.getDefaultInstance();
362       return this;
363     }
364 
365     @Override
366     public Builder mergeFrom(Message other) {
367       if (other instanceof DynamicMessage) {
368         // This should be somewhat faster than calling super.mergeFrom().
369         DynamicMessage otherDynamicMessage = (DynamicMessage) other;
370         if (otherDynamicMessage.type != type) {
371           throw new IllegalArgumentException(
372               "mergeFrom(Message) can only merge messages of the same type.");
373         }
374         ensureIsMutable();
375         fields.mergeFrom(otherDynamicMessage.fields);
376         mergeUnknownFields(otherDynamicMessage.unknownFields);
377         for (int i = 0; i < oneofCases.length; i++) {
378           if (oneofCases[i] == null) {
379             oneofCases[i] = otherDynamicMessage.oneofCases[i];
380           } else {
381             if ((otherDynamicMessage.oneofCases[i] != null)
382                 && (oneofCases[i] != otherDynamicMessage.oneofCases[i])) {
383               fields.clearField(oneofCases[i]);
384               oneofCases[i] = otherDynamicMessage.oneofCases[i];
385             }
386           }
387         }
388         return this;
389       } else {
390         return super.mergeFrom(other);
391       }
392     }
393 
394     @Override
395     public DynamicMessage build() {
396       if (!isInitialized()) {
397         throw newUninitializedMessageException(
398             new DynamicMessage(
399                 type,
400                 fields,
401                 java.util.Arrays.copyOf(oneofCases, oneofCases.length),
402                 unknownFields));
403       }
404       return buildPartial();
405     }
406 
407     /**
408      * Helper for DynamicMessage.parseFrom() methods to call. Throws {@link
409      * InvalidProtocolBufferException} instead of {@link UninitializedMessageException}.
410      */
411     private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
412       if (!isInitialized()) {
413         throw newUninitializedMessageException(
414                 new DynamicMessage(
415                     type,
416                     fields,
417                     java.util.Arrays.copyOf(oneofCases, oneofCases.length),
418                     unknownFields))
419             .asInvalidProtocolBufferException();
420       }
421       return buildPartial();
422     }
423 
424     @Override
425     public DynamicMessage buildPartial() {
426       fields.makeImmutable();
427       DynamicMessage result =
428           new DynamicMessage(
429               type, fields, java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields);
430       return result;
431     }
432 
433     @Override
434     public Builder clone() {
435       Builder result = new Builder(type);
436       result.fields.mergeFrom(fields);
437       result.mergeUnknownFields(unknownFields);
438       System.arraycopy(oneofCases, 0, result.oneofCases, 0, oneofCases.length);
439       return result;
440     }
441 
442     @Override
443     public boolean isInitialized() {
444       return DynamicMessage.isInitialized(type, fields);
445     }
446 
447     @Override
448     public Descriptor getDescriptorForType() {
449       return type;
450     }
451 
452     @Override
453     public DynamicMessage getDefaultInstanceForType() {
454       return getDefaultInstance(type);
455     }
456 
457     @Override
458     public Map<FieldDescriptor, Object> getAllFields() {
459       return fields.getAllFields();
460     }
461 
462     @Override
463     public Builder newBuilderForField(FieldDescriptor field) {
464       verifyContainingType(field);
465 
466       if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
467         throw new IllegalArgumentException(
468             "newBuilderForField is only valid for fields with message type.");
469       }
470 
471       return new Builder(field.getMessageType());
472     }
473 
474     @Override
475     public boolean hasOneof(OneofDescriptor oneof) {
476       verifyOneofContainingType(oneof);
477       FieldDescriptor field = oneofCases[oneof.getIndex()];
478       if (field == null) {
479         return false;
480       }
481       return true;
482     }
483 
484     @Override
485     public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
486       verifyOneofContainingType(oneof);
487       return oneofCases[oneof.getIndex()];
488     }
489 
490     @Override
491     public Builder clearOneof(OneofDescriptor oneof) {
492       verifyOneofContainingType(oneof);
493       FieldDescriptor field = oneofCases[oneof.getIndex()];
494       if (field != null) {
495         clearField(field);
496       }
497       return this;
498     }
499 
500     @Override
501     public boolean hasField(FieldDescriptor field) {
502       verifyContainingType(field);
503       return fields.hasField(field);
504     }
505 
506     @Override
507     public Object getField(FieldDescriptor field) {
508       verifyContainingType(field);
509       Object result = fields.getField(field);
510       if (result == null) {
511         if (field.isRepeated()) {
512           result = Collections.emptyList();
513         } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
514           result = getDefaultInstance(field.getMessageType());
515         } else {
516           result = field.getDefaultValue();
517         }
518       }
519       return result;
520     }
521 
522     @Override
523     public Builder setField(FieldDescriptor field, Object value) {
524       verifyContainingType(field);
525       ensureIsMutable();
526       // TODO(xiaofeng): This check should really be put in FieldSet.setField()
527       // where all other such checks are done. However, currently
528       // FieldSet.setField() permits Integer value for enum fields probably
529       // because of some internal features we support. Should figure it out
530       // and move this check to a more appropriate place.
531       if (field.getType() == FieldDescriptor.Type.ENUM) {
532         ensureEnumValueDescriptor(field, value);
533       }
534       OneofDescriptor oneofDescriptor = field.getContainingOneof();
535       if (oneofDescriptor != null) {
536         int index = oneofDescriptor.getIndex();
537         FieldDescriptor oldField = oneofCases[index];
538         if ((oldField != null) && (oldField != field)) {
539           fields.clearField(oldField);
540         }
541         oneofCases[index] = field;
542       } else if (field.getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3) {
543         if (!field.isRepeated()
544             && field.getJavaType() != FieldDescriptor.JavaType.MESSAGE
545             && value.equals(field.getDefaultValue())) {
546           // In proto3, setting a field to its default value is equivalent to clearing the field.
547           fields.clearField(field);
548           return this;
549         }
550       }
551       fields.setField(field, value);
552       return this;
553     }
554 
555     @Override
556     public Builder clearField(FieldDescriptor field) {
557       verifyContainingType(field);
558       ensureIsMutable();
559       OneofDescriptor oneofDescriptor = field.getContainingOneof();
560       if (oneofDescriptor != null) {
561         int index = oneofDescriptor.getIndex();
562         if (oneofCases[index] == field) {
563           oneofCases[index] = null;
564         }
565       }
566       fields.clearField(field);
567       return this;
568     }
569 
570     @Override
571     public int getRepeatedFieldCount(FieldDescriptor field) {
572       verifyContainingType(field);
573       return fields.getRepeatedFieldCount(field);
574     }
575 
576     @Override
577     public Object getRepeatedField(FieldDescriptor field, int index) {
578       verifyContainingType(field);
579       return fields.getRepeatedField(field, index);
580     }
581 
582     @Override
583     public Builder setRepeatedField(FieldDescriptor field, int index, Object value) {
584       verifyContainingType(field);
585       ensureIsMutable();
586       fields.setRepeatedField(field, index, value);
587       return this;
588     }
589 
590     @Override
591     public Builder addRepeatedField(FieldDescriptor field, Object value) {
592       verifyContainingType(field);
593       ensureIsMutable();
594       fields.addRepeatedField(field, value);
595       return this;
596     }
597 
598     @Override
599     public UnknownFieldSet getUnknownFields() {
600       return unknownFields;
601     }
602 
603     @Override
604     public Builder setUnknownFields(UnknownFieldSet unknownFields) {
605       this.unknownFields = unknownFields;
606       return this;
607     }
608 
609     @Override
610     public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
611       this.unknownFields =
612           UnknownFieldSet.newBuilder(this.unknownFields).mergeFrom(unknownFields).build();
613       return this;
614     }
615 
616     /** Verifies that the field is a field of this message. */
617     private void verifyContainingType(FieldDescriptor field) {
618       if (field.getContainingType() != type) {
619         throw new IllegalArgumentException("FieldDescriptor does not match message type.");
620       }
621     }
622 
623     /** Verifies that the oneof is an oneof of this message. */
624     private void verifyOneofContainingType(OneofDescriptor oneof) {
625       if (oneof.getContainingType() != type) {
626         throw new IllegalArgumentException("OneofDescriptor does not match message type.");
627       }
628     }
629 
630     /** Verifies that the value is EnumValueDescriptor and matches Enum Type. */
631     private void ensureSingularEnumValueDescriptor(FieldDescriptor field, Object value) {
632       checkNotNull(value);
633       if (!(value instanceof EnumValueDescriptor)) {
634         throw new IllegalArgumentException(
635             "DynamicMessage should use EnumValueDescriptor to set Enum Value.");
636       }
637       // TODO(xiaofeng): Re-enable this check after Orgstore is fixed to not
638       // set incorrect EnumValueDescriptors.
639       // EnumDescriptor fieldType = field.getEnumType();
640       // EnumDescriptor fieldValueType = ((EnumValueDescriptor) value).getType();
641       // if (fieldType != fieldValueType) {
642       //  throw new IllegalArgumentException(String.format(
643       //      "EnumDescriptor %s of field doesn't match EnumDescriptor %s of field value",
644       //      fieldType.getFullName(), fieldValueType.getFullName()));
645       // }
646     }
647 
648     /** Verifies the value for an enum field. */
649     private void ensureEnumValueDescriptor(FieldDescriptor field, Object value) {
650       if (field.isRepeated()) {
651         for (Object item : (List) value) {
652           ensureSingularEnumValueDescriptor(field, item);
653         }
654       } else {
655         ensureSingularEnumValueDescriptor(field, value);
656       }
657     }
658 
659     private void ensureIsMutable() {
660       if (fields.isImmutable()) {
661         fields = fields.clone();
662       }
663     }
664 
665     @Override
666     public com.google.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) {
667       // TODO(xiangl): need implementation for dynamic message
668       throw new UnsupportedOperationException(
669           "getFieldBuilder() called on a dynamic message type.");
670     }
671 
672     @Override
673     public com.google.protobuf.Message.Builder getRepeatedFieldBuilder(
674         FieldDescriptor field, int index) {
675       throw new UnsupportedOperationException(
676           "getRepeatedFieldBuilder() called on a dynamic message type.");
677     }
678   }
679 }
680