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