• 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     }
332 
333     // ---------------------------------------------------------------
334     // Implementation of Message.Builder interface.
335 
336     @Override
337     public Builder clear() {
338       if (fields.isImmutable()) {
339         fields = FieldSet.newFieldSet();
340       } else {
341         fields.clear();
342       }
343       unknownFields = UnknownFieldSet.getDefaultInstance();
344       return this;
345     }
346 
347     @Override
348     public Builder mergeFrom(Message other) {
349       if (other instanceof DynamicMessage) {
350         // This should be somewhat faster than calling super.mergeFrom().
351         DynamicMessage otherDynamicMessage = (DynamicMessage) other;
352         if (otherDynamicMessage.type != type) {
353           throw new IllegalArgumentException(
354               "mergeFrom(Message) can only merge messages of the same type.");
355         }
356         ensureIsMutable();
357         fields.mergeFrom(otherDynamicMessage.fields);
358         mergeUnknownFields(otherDynamicMessage.unknownFields);
359         for (int i = 0; i < oneofCases.length; i++) {
360           if (oneofCases[i] == null) {
361             oneofCases[i] = otherDynamicMessage.oneofCases[i];
362           } else {
363             if ((otherDynamicMessage.oneofCases[i] != null)
364                 && (oneofCases[i] != otherDynamicMessage.oneofCases[i])) {
365               fields.clearField(oneofCases[i]);
366               oneofCases[i] = otherDynamicMessage.oneofCases[i];
367             }
368           }
369         }
370         return this;
371       } else {
372         return super.mergeFrom(other);
373       }
374     }
375 
376     @Override
377     public DynamicMessage build() {
378       if (!isInitialized()) {
379         throw newUninitializedMessageException(
380             new DynamicMessage(
381                 type,
382                 fields,
383                 java.util.Arrays.copyOf(oneofCases, oneofCases.length),
384                 unknownFields));
385       }
386       return buildPartial();
387     }
388 
389     /**
390      * Helper for DynamicMessage.parseFrom() methods to call. Throws {@link
391      * InvalidProtocolBufferException} instead of {@link UninitializedMessageException}.
392      */
393     private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
394       if (!isInitialized()) {
395         throw newUninitializedMessageException(
396                 new DynamicMessage(
397                     type,
398                     fields,
399                     java.util.Arrays.copyOf(oneofCases, oneofCases.length),
400                     unknownFields))
401             .asInvalidProtocolBufferException();
402       }
403       return buildPartial();
404     }
405 
406     @Override
407     public DynamicMessage buildPartial() {
408       // Set default values for all fields in a MapEntry.
409       if (type.getOptions().getMapEntry()) {
410         for (FieldDescriptor field : type.getFields()) {
411           if (field.isOptional() && !fields.hasField(field)) {
412             if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
413               fields.setField(field, getDefaultInstance(field.getMessageType()));
414             } else {
415               fields.setField(field, field.getDefaultValue());
416             }
417           }
418         }
419       }
420 
421       fields.makeImmutable();
422       DynamicMessage result =
423           new DynamicMessage(
424               type,
425               fields,
426               java.util.Arrays.copyOf(oneofCases, oneofCases.length),
427               unknownFields);
428       return result;
429     }
430 
431     @Override
432     public Builder clone() {
433       Builder result = new Builder(type);
434       result.fields.mergeFrom(fields);
435       result.mergeUnknownFields(unknownFields);
436       System.arraycopy(oneofCases, 0, result.oneofCases, 0, oneofCases.length);
437       return result;
438     }
439 
440     @Override
441     public boolean isInitialized() {
442       return DynamicMessage.isInitialized(type, fields);
443     }
444 
445     @Override
446     public Descriptor getDescriptorForType() {
447       return type;
448     }
449 
450     @Override
451     public DynamicMessage getDefaultInstanceForType() {
452       return getDefaultInstance(type);
453     }
454 
455     @Override
456     public Map<FieldDescriptor, Object> getAllFields() {
457       return fields.getAllFields();
458     }
459 
460     @Override
461     public Builder newBuilderForField(FieldDescriptor field) {
462       verifyContainingType(field);
463 
464       if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
465         throw new IllegalArgumentException(
466             "newBuilderForField is only valid for fields with message type.");
467       }
468 
469       return new Builder(field.getMessageType());
470     }
471 
472     @Override
473     public boolean hasOneof(OneofDescriptor oneof) {
474       verifyOneofContainingType(oneof);
475       FieldDescriptor field = oneofCases[oneof.getIndex()];
476       if (field == null) {
477         return false;
478       }
479       return true;
480     }
481 
482     @Override
483     public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
484       verifyOneofContainingType(oneof);
485       return oneofCases[oneof.getIndex()];
486     }
487 
488     @Override
489     public Builder clearOneof(OneofDescriptor oneof) {
490       verifyOneofContainingType(oneof);
491       FieldDescriptor field = oneofCases[oneof.getIndex()];
492       if (field != null) {
493         clearField(field);
494       }
495       return this;
496     }
497 
498     @Override
499     public boolean hasField(FieldDescriptor field) {
500       verifyContainingType(field);
501       return fields.hasField(field);
502     }
503 
504     @Override
505     public Object getField(FieldDescriptor field) {
506       verifyContainingType(field);
507       Object result = fields.getField(field);
508       if (result == null) {
509         if (field.isRepeated()) {
510           result = Collections.emptyList();
511         } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
512           result = getDefaultInstance(field.getMessageType());
513         } else {
514           result = field.getDefaultValue();
515         }
516       }
517       return result;
518     }
519 
520     @Override
521     public Builder setField(FieldDescriptor field, Object value) {
522       verifyContainingType(field);
523       ensureIsMutable();
524       // TODO(xiaofeng): This check should really be put in FieldSet.setField()
525       // where all other such checks are done. However, currently
526       // FieldSet.setField() permits Integer value for enum fields probably
527       // because of some internal features we support. Should figure it out
528       // and move this check to a more appropriate place.
529       if (field.getType() == FieldDescriptor.Type.ENUM) {
530         ensureEnumValueDescriptor(field, value);
531       }
532       OneofDescriptor oneofDescriptor = field.getContainingOneof();
533       if (oneofDescriptor != null) {
534         int index = oneofDescriptor.getIndex();
535         FieldDescriptor oldField = oneofCases[index];
536         if ((oldField != null) && (oldField != field)) {
537           fields.clearField(oldField);
538         }
539         oneofCases[index] = field;
540       } else if (field.getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3) {
541         if (!field.isRepeated()
542             && field.getJavaType() != FieldDescriptor.JavaType.MESSAGE
543             && value.equals(field.getDefaultValue())) {
544           // In proto3, setting a field to its default value is equivalent to clearing the field.
545           fields.clearField(field);
546           return this;
547         }
548       }
549       fields.setField(field, value);
550       return this;
551     }
552 
553     @Override
554     public Builder clearField(FieldDescriptor field) {
555       verifyContainingType(field);
556       ensureIsMutable();
557       OneofDescriptor oneofDescriptor = field.getContainingOneof();
558       if (oneofDescriptor != null) {
559         int index = oneofDescriptor.getIndex();
560         if (oneofCases[index] == field) {
561           oneofCases[index] = null;
562         }
563       }
564       fields.clearField(field);
565       return this;
566     }
567 
568     @Override
569     public int getRepeatedFieldCount(FieldDescriptor field) {
570       verifyContainingType(field);
571       return fields.getRepeatedFieldCount(field);
572     }
573 
574     @Override
575     public Object getRepeatedField(FieldDescriptor field, int index) {
576       verifyContainingType(field);
577       return fields.getRepeatedField(field, index);
578     }
579 
580     @Override
581     public Builder setRepeatedField(FieldDescriptor field, int index, Object value) {
582       verifyContainingType(field);
583       ensureIsMutable();
584       fields.setRepeatedField(field, index, value);
585       return this;
586     }
587 
588     @Override
589     public Builder addRepeatedField(FieldDescriptor field, Object value) {
590       verifyContainingType(field);
591       ensureIsMutable();
592       fields.addRepeatedField(field, value);
593       return this;
594     }
595 
596     @Override
597     public UnknownFieldSet getUnknownFields() {
598       return unknownFields;
599     }
600 
601     @Override
602     public Builder setUnknownFields(UnknownFieldSet unknownFields) {
603       this.unknownFields = unknownFields;
604       return this;
605     }
606 
607     @Override
608     public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
609       this.unknownFields =
610           UnknownFieldSet.newBuilder(this.unknownFields).mergeFrom(unknownFields).build();
611       return this;
612     }
613 
614     /** Verifies that the field is a field of this message. */
615     private void verifyContainingType(FieldDescriptor field) {
616       if (field.getContainingType() != type) {
617         throw new IllegalArgumentException("FieldDescriptor does not match message type.");
618       }
619     }
620 
621     /** Verifies that the oneof is an oneof of this message. */
622     private void verifyOneofContainingType(OneofDescriptor oneof) {
623       if (oneof.getContainingType() != type) {
624         throw new IllegalArgumentException("OneofDescriptor does not match message type.");
625       }
626     }
627 
628     /** Verifies that the value is EnumValueDescriptor and matches Enum Type. */
629     private void ensureSingularEnumValueDescriptor(FieldDescriptor field, Object value) {
630       checkNotNull(value);
631       if (!(value instanceof EnumValueDescriptor)) {
632         throw new IllegalArgumentException(
633             "DynamicMessage should use EnumValueDescriptor to set Enum Value.");
634       }
635       // TODO(xiaofeng): Re-enable this check after Orgstore is fixed to not
636       // set incorrect EnumValueDescriptors.
637       // EnumDescriptor fieldType = field.getEnumType();
638       // EnumDescriptor fieldValueType = ((EnumValueDescriptor) value).getType();
639       // if (fieldType != fieldValueType) {
640       //  throw new IllegalArgumentException(String.format(
641       //      "EnumDescriptor %s of field doesn't match EnumDescriptor %s of field value",
642       //      fieldType.getFullName(), fieldValueType.getFullName()));
643       // }
644     }
645 
646     /** Verifies the value for an enum field. */
647     private void ensureEnumValueDescriptor(FieldDescriptor field, Object value) {
648       if (field.isRepeated()) {
649         for (Object item : (List) value) {
650           ensureSingularEnumValueDescriptor(field, item);
651         }
652       } else {
653         ensureSingularEnumValueDescriptor(field, value);
654       }
655     }
656 
657     private void ensureIsMutable() {
658       if (fields.isImmutable()) {
659         fields = fields.clone();
660       }
661     }
662 
663     @Override
664     public com.google.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) {
665       // TODO(xiangl): need implementation for dynamic message
666       throw new UnsupportedOperationException(
667           "getFieldBuilder() called on a dynamic message type.");
668     }
669 
670     @Override
671     public com.google.protobuf.Message.Builder getRepeatedFieldBuilder(
672         FieldDescriptor field, int index) {
673       throw new UnsupportedOperationException(
674           "getRepeatedFieldBuilder() called on a dynamic message type.");
675     }
676   }
677 }
678