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