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       }
530       fields.setField(field, value);
531       return this;
532     }
533 
534     @Override
535     public Builder clearField(FieldDescriptor field) {
536       verifyContainingType(field);
537       ensureIsMutable();
538       OneofDescriptor oneofDescriptor = field.getContainingOneof();
539       if (oneofDescriptor != null) {
540         int index = oneofDescriptor.getIndex();
541         if (oneofCases[index] == field) {
542           oneofCases[index] = null;
543         }
544       }
545       fields.clearField(field);
546       return this;
547     }
548 
549     @Override
550     public int getRepeatedFieldCount(FieldDescriptor field) {
551       verifyContainingType(field);
552       return fields.getRepeatedFieldCount(field);
553     }
554 
555     @Override
556     public Object getRepeatedField(FieldDescriptor field, int index) {
557       verifyContainingType(field);
558       return fields.getRepeatedField(field, index);
559     }
560 
561     @Override
562     public Builder setRepeatedField(FieldDescriptor field, int index, Object value) {
563       verifyContainingType(field);
564       ensureIsMutable();
565       fields.setRepeatedField(field, index, value);
566       return this;
567     }
568 
569     @Override
570     public Builder addRepeatedField(FieldDescriptor field, Object value) {
571       verifyContainingType(field);
572       ensureIsMutable();
573       fields.addRepeatedField(field, value);
574       return this;
575     }
576 
577     @Override
578     public UnknownFieldSet getUnknownFields() {
579       return unknownFields;
580     }
581 
582     @Override
583     public Builder setUnknownFields(UnknownFieldSet unknownFields) {
584       if (getDescriptorForType().getFile().getSyntax()
585           == Descriptors.FileDescriptor.Syntax.PROTO3) {
586         // Proto3 discards unknown fields.
587         return this;
588       }
589       this.unknownFields = unknownFields;
590       return this;
591     }
592 
593     @Override
594     public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
595       if (getDescriptorForType().getFile().getSyntax()
596           == Descriptors.FileDescriptor.Syntax.PROTO3) {
597         // Proto3 discards unknown fields.
598         return this;
599       }
600       this.unknownFields =
601         UnknownFieldSet.newBuilder(this.unknownFields)
602                        .mergeFrom(unknownFields)
603                        .build();
604       return this;
605     }
606 
607     /** Verifies that the field is a field of this message. */
608     private void verifyContainingType(FieldDescriptor field) {
609       if (field.getContainingType() != type) {
610         throw new IllegalArgumentException(
611           "FieldDescriptor does not match message type.");
612       }
613     }
614 
615     /** Verifies that the oneof is an oneof of this message. */
616     private void verifyOneofContainingType(OneofDescriptor oneof) {
617       if (oneof.getContainingType() != type) {
618         throw new IllegalArgumentException(
619           "OneofDescriptor does not match message type.");
620       }
621     }
622 
623     /** Verifies that the value is EnumValueDescriptor and matches Enum Type. */
624     private void ensureSingularEnumValueDescriptor(
625         FieldDescriptor field, Object value) {
626       if (value == null) {
627         throw new NullPointerException();
628       }
629       if (!(value instanceof EnumValueDescriptor)) {
630         throw new IllegalArgumentException(
631           "DynamicMessage should use EnumValueDescriptor to set Enum Value.");
632       }
633       // TODO(xiaofeng): Re-enable this check after Orgstore is fixed to not
634       // set incorrect EnumValueDescriptors.
635       // EnumDescriptor fieldType = field.getEnumType();
636       // EnumDescriptor fieldValueType = ((EnumValueDescriptor) value).getType();
637       // if (fieldType != fieldValueType) {
638       //  throw new IllegalArgumentException(String.format(
639       //      "EnumDescriptor %s of field doesn't match EnumDescriptor %s of field value",
640       //      fieldType.getFullName(), fieldValueType.getFullName()));
641       // }
642     }
643 
644     /** Verifies the value for an enum field. */
645     private void ensureEnumValueDescriptor(
646         FieldDescriptor field, Object value) {
647       if (field.isRepeated()) {
648         for (Object item : (List) value) {
649           ensureSingularEnumValueDescriptor(field, item);
650         }
651       } else {
652          ensureSingularEnumValueDescriptor(field, value);
653       }
654     }
655 
656     private void ensureIsMutable() {
657       if (fields.isImmutable()) {
658         fields = fields.clone();
659       }
660     }
661 
662     @Override
663     public com.google.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) {
664       // TODO(xiangl): need implementation for dynamic message
665       throw new UnsupportedOperationException(
666         "getFieldBuilder() called on a dynamic message type.");
667     }
668 
669     @Override
670     public com.google.protobuf.Message.Builder getRepeatedFieldBuilder(FieldDescriptor field,
671         int index) {
672       throw new UnsupportedOperationException(
673         "getRepeatedFieldBuilder() called on a dynamic message type.");
674     }
675   }
676 }
677