1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // http://code.google.com/p/protobuf/ 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 java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.Iterator; 36 import java.util.TreeMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.io.IOException; 40 41 /** 42 * A class which represents an arbitrary set of fields of some message type. 43 * This is used to implement {@link DynamicMessage}, and also to represent 44 * extensions in {@link GeneratedMessage}. This class is package-private, 45 * since outside users should probably be using {@link DynamicMessage}. 46 * 47 * @author kenton (at) google.com Kenton Varda 48 */ 49 final class FieldSet<FieldDescriptorType extends 50 FieldSet.FieldDescriptorLite<FieldDescriptorType>> { 51 /** 52 * Interface for a FieldDescriptor or lite extension descriptor. This 53 * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}. 54 */ 55 public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>> 56 extends Comparable<T> { 57 int getNumber(); 58 WireFormat.FieldType getLiteType(); 59 WireFormat.JavaType getLiteJavaType(); 60 boolean isRepeated(); 61 boolean isPacked(); 62 Internal.EnumLiteMap<?> getEnumType(); 63 64 // If getLiteJavaType() == MESSAGE, this merges a message object of the 65 // type into a builder of the type. Returns {@code to}. 66 MessageLite.Builder internalMergeFrom( 67 MessageLite.Builder to, MessageLite from); 68 } 69 70 private Map<FieldDescriptorType, Object> fields; 71 72 /** Construct a new FieldSet. */ 73 private FieldSet() { 74 // Use a TreeMap because fields need to be in canonical order when 75 // serializing. 76 // TODO(kenton): Maybe use some sort of sparse array instead? It would 77 // even make sense to store the first 16 or so tags in a flat array 78 // to make DynamicMessage faster. 79 fields = new TreeMap<FieldDescriptorType, Object>(); 80 } 81 82 /** 83 * Construct an empty FieldSet. This is only used to initialize 84 * DEFAULT_INSTANCE. 85 */ 86 private FieldSet(final boolean dummy) { 87 this.fields = Collections.emptyMap(); 88 } 89 90 /** Construct a new FieldSet. */ 91 public static <T extends FieldSet.FieldDescriptorLite<T>> 92 FieldSet<T> newFieldSet() { 93 return new FieldSet<T>(); 94 } 95 96 /** Get an immutable empty FieldSet. */ 97 @SuppressWarnings("unchecked") 98 public static <T extends FieldSet.FieldDescriptorLite<T>> 99 FieldSet<T> emptySet() { 100 return DEFAULT_INSTANCE; 101 } 102 @SuppressWarnings("unchecked") 103 private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); 104 105 /** Make this FieldSet immutable from this point forward. */ 106 @SuppressWarnings("unchecked") 107 public void makeImmutable() { 108 for (final Map.Entry<FieldDescriptorType, Object> entry: 109 fields.entrySet()) { 110 if (entry.getKey().isRepeated()) { 111 final List value = (List)entry.getValue(); 112 fields.put(entry.getKey(), Collections.unmodifiableList(value)); 113 } 114 } 115 fields = Collections.unmodifiableMap(fields); 116 } 117 118 // ================================================================= 119 120 /** See {@link Message.Builder#clear()}. */ 121 public void clear() { 122 fields.clear(); 123 } 124 125 /** 126 * Get a simple map containing all the fields. 127 */ 128 public Map<FieldDescriptorType, Object> getAllFields() { 129 return Collections.unmodifiableMap(fields); 130 } 131 132 /** 133 * Get an iterator to the field map. This iterator should not be leaked 134 * out of the protobuf library as it is not protected from mutation. 135 */ 136 public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { 137 return fields.entrySet().iterator(); 138 } 139 140 /** 141 * Useful for implementing 142 * {@link Message#hasField(Descriptors.FieldDescriptor)}. 143 */ 144 public boolean hasField(final FieldDescriptorType descriptor) { 145 if (descriptor.isRepeated()) { 146 throw new IllegalArgumentException( 147 "hasField() can only be called on non-repeated fields."); 148 } 149 150 return fields.get(descriptor) != null; 151 } 152 153 /** 154 * Useful for implementing 155 * {@link Message#getField(Descriptors.FieldDescriptor)}. This method 156 * returns {@code null} if the field is not set; in this case it is up 157 * to the caller to fetch the field's default value. 158 */ 159 public Object getField(final FieldDescriptorType descriptor) { 160 return fields.get(descriptor); 161 } 162 163 /** 164 * Useful for implementing 165 * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. 166 */ 167 @SuppressWarnings("unchecked") 168 public void setField(final FieldDescriptorType descriptor, 169 Object value) { 170 if (descriptor.isRepeated()) { 171 if (!(value instanceof List)) { 172 throw new IllegalArgumentException( 173 "Wrong object type used with protocol message reflection."); 174 } 175 176 // Wrap the contents in a new list so that the caller cannot change 177 // the list's contents after setting it. 178 final List newList = new ArrayList(); 179 newList.addAll((List)value); 180 for (final Object element : newList) { 181 verifyType(descriptor.getLiteType(), element); 182 } 183 value = newList; 184 } else { 185 verifyType(descriptor.getLiteType(), value); 186 } 187 188 fields.put(descriptor, value); 189 } 190 191 /** 192 * Useful for implementing 193 * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. 194 */ 195 public void clearField(final FieldDescriptorType descriptor) { 196 fields.remove(descriptor); 197 } 198 199 /** 200 * Useful for implementing 201 * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. 202 */ 203 public int getRepeatedFieldCount(final FieldDescriptorType descriptor) { 204 if (!descriptor.isRepeated()) { 205 throw new IllegalArgumentException( 206 "getRepeatedField() can only be called on repeated fields."); 207 } 208 209 final Object value = fields.get(descriptor); 210 if (value == null) { 211 return 0; 212 } else { 213 return ((List) value).size(); 214 } 215 } 216 217 /** 218 * Useful for implementing 219 * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. 220 */ 221 public Object getRepeatedField(final FieldDescriptorType descriptor, 222 final int index) { 223 if (!descriptor.isRepeated()) { 224 throw new IllegalArgumentException( 225 "getRepeatedField() can only be called on repeated fields."); 226 } 227 228 final Object value = fields.get(descriptor); 229 230 if (value == null) { 231 throw new IndexOutOfBoundsException(); 232 } else { 233 return ((List) value).get(index); 234 } 235 } 236 237 /** 238 * Useful for implementing 239 * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. 240 */ 241 @SuppressWarnings("unchecked") 242 public void setRepeatedField(final FieldDescriptorType descriptor, 243 final int index, 244 final Object value) { 245 if (!descriptor.isRepeated()) { 246 throw new IllegalArgumentException( 247 "getRepeatedField() can only be called on repeated fields."); 248 } 249 250 final Object list = fields.get(descriptor); 251 if (list == null) { 252 throw new IndexOutOfBoundsException(); 253 } 254 255 verifyType(descriptor.getLiteType(), value); 256 ((List) list).set(index, value); 257 } 258 259 /** 260 * Useful for implementing 261 * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. 262 */ 263 @SuppressWarnings("unchecked") 264 public void addRepeatedField(final FieldDescriptorType descriptor, 265 final Object value) { 266 if (!descriptor.isRepeated()) { 267 throw new IllegalArgumentException( 268 "addRepeatedField() can only be called on repeated fields."); 269 } 270 271 verifyType(descriptor.getLiteType(), value); 272 273 final Object existingValue = fields.get(descriptor); 274 List list; 275 if (existingValue == null) { 276 list = new ArrayList(); 277 fields.put(descriptor, list); 278 } else { 279 list = (List) existingValue; 280 } 281 282 list.add(value); 283 } 284 285 /** 286 * Verifies that the given object is of the correct type to be a valid 287 * value for the given field. (For repeated fields, this checks if the 288 * object is the right type to be one element of the field.) 289 * 290 * @throws IllegalArgumentException The value is not of the right type. 291 */ 292 private static void verifyType(final WireFormat.FieldType type, 293 final Object value) { 294 if (value == null) { 295 throw new NullPointerException(); 296 } 297 298 boolean isValid = false; 299 switch (type.getJavaType()) { 300 case INT: isValid = value instanceof Integer ; break; 301 case LONG: isValid = value instanceof Long ; break; 302 case FLOAT: isValid = value instanceof Float ; break; 303 case DOUBLE: isValid = value instanceof Double ; break; 304 case BOOLEAN: isValid = value instanceof Boolean ; break; 305 case STRING: isValid = value instanceof String ; break; 306 case BYTE_STRING: isValid = value instanceof ByteString; break; 307 case ENUM: 308 // TODO(kenton): Caller must do type checking here, I guess. 309 isValid = value instanceof Internal.EnumLite; 310 break; 311 case MESSAGE: 312 // TODO(kenton): Caller must do type checking here, I guess. 313 isValid = value instanceof MessageLite; 314 break; 315 } 316 317 if (!isValid) { 318 // TODO(kenton): When chaining calls to setField(), it can be hard to 319 // tell from the stack trace which exact call failed, since the whole 320 // chain is considered one line of code. It would be nice to print 321 // more information here, e.g. naming the field. We used to do that. 322 // But we can't now that FieldSet doesn't use descriptors. Maybe this 323 // isn't a big deal, though, since it would only really apply when using 324 // reflection and generally people don't chain reflection setters. 325 throw new IllegalArgumentException( 326 "Wrong object type used with protocol message reflection."); 327 } 328 } 329 330 // ================================================================= 331 // Parsing and serialization 332 333 /** 334 * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} 335 * itself does not have any way of knowing about required fields that 336 * aren't actually present in the set, it is up to the caller to check 337 * that all required fields are present. 338 */ 339 @SuppressWarnings("unchecked") 340 public boolean isInitialized() { 341 for (final Map.Entry<FieldDescriptorType, Object> entry: 342 fields.entrySet()) { 343 final FieldDescriptorType descriptor = entry.getKey(); 344 if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { 345 if (descriptor.isRepeated()) { 346 for (final MessageLite element: 347 (List<MessageLite>) entry.getValue()) { 348 if (!element.isInitialized()) { 349 return false; 350 } 351 } 352 } else { 353 if (!((MessageLite) entry.getValue()).isInitialized()) { 354 return false; 355 } 356 } 357 } 358 } 359 360 return true; 361 } 362 363 /** 364 * Given a field type, return the wire type. 365 * 366 * @returns One of the {@code WIRETYPE_} constants defined in 367 * {@link WireFormat}. 368 */ 369 static int getWireFormatForFieldType(final WireFormat.FieldType type, 370 boolean isPacked) { 371 if (isPacked) { 372 return WireFormat.WIRETYPE_LENGTH_DELIMITED; 373 } else { 374 return type.getWireType(); 375 } 376 } 377 378 /** 379 * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. 380 */ 381 @SuppressWarnings("unchecked") 382 public void mergeFrom(final FieldSet<FieldDescriptorType> other) { 383 for (final Map.Entry<FieldDescriptorType, Object> entry: 384 other.fields.entrySet()) { 385 final FieldDescriptorType descriptor = entry.getKey(); 386 final Object otherValue = entry.getValue(); 387 388 if (descriptor.isRepeated()) { 389 Object value = fields.get(descriptor); 390 if (value == null) { 391 // Our list is empty, but we still need to make a defensive copy of 392 // the other list since we don't know if the other FieldSet is still 393 // mutable. 394 fields.put(descriptor, new ArrayList((List) otherValue)); 395 } else { 396 // Concatenate the lists. 397 ((List) value).addAll((List) otherValue); 398 } 399 } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { 400 Object value = fields.get(descriptor); 401 if (value == null) { 402 fields.put(descriptor, otherValue); 403 } else { 404 // Merge the messages. 405 fields.put(descriptor, 406 descriptor.internalMergeFrom( 407 ((MessageLite) value).toBuilder(), (MessageLite) otherValue) 408 .build()); 409 } 410 411 } else { 412 fields.put(descriptor, otherValue); 413 } 414 } 415 } 416 417 // TODO(kenton): Move static parsing and serialization methods into some 418 // other class. Probably WireFormat. 419 420 /** 421 * Read a field of any primitive type from a CodedInputStream. Enums, 422 * groups, and embedded messages are not handled by this method. 423 * 424 * @param input The stream from which to read. 425 * @param type Declared type of the field. 426 * @return An object representing the field's value, of the exact 427 * type which would be returned by 428 * {@link Message#getField(Descriptors.FieldDescriptor)} for 429 * this field. 430 */ 431 public static Object readPrimitiveField( 432 CodedInputStream input, 433 final WireFormat.FieldType type) throws IOException { 434 switch (type) { 435 case DOUBLE : return input.readDouble (); 436 case FLOAT : return input.readFloat (); 437 case INT64 : return input.readInt64 (); 438 case UINT64 : return input.readUInt64 (); 439 case INT32 : return input.readInt32 (); 440 case FIXED64 : return input.readFixed64 (); 441 case FIXED32 : return input.readFixed32 (); 442 case BOOL : return input.readBool (); 443 case STRING : return input.readString (); 444 case BYTES : return input.readBytes (); 445 case UINT32 : return input.readUInt32 (); 446 case SFIXED32: return input.readSFixed32(); 447 case SFIXED64: return input.readSFixed64(); 448 case SINT32 : return input.readSInt32 (); 449 case SINT64 : return input.readSInt64 (); 450 451 case GROUP: 452 throw new IllegalArgumentException( 453 "readPrimitiveField() cannot handle nested groups."); 454 case MESSAGE: 455 throw new IllegalArgumentException( 456 "readPrimitiveField() cannot handle embedded messages."); 457 case ENUM: 458 // We don't handle enums because we don't know what to do if the 459 // value is not recognized. 460 throw new IllegalArgumentException( 461 "readPrimitiveField() cannot handle enums."); 462 } 463 464 throw new RuntimeException( 465 "There is no way to get here, but the compiler thinks otherwise."); 466 } 467 468 /** See {@link Message#writeTo(CodedOutputStream)}. */ 469 public void writeTo(final CodedOutputStream output) 470 throws IOException { 471 for (final Map.Entry<FieldDescriptorType, Object> entry: 472 fields.entrySet()) { 473 writeField(entry.getKey(), entry.getValue(), output); 474 } 475 } 476 477 /** 478 * Like {@link #writeTo} but uses MessageSet wire format. 479 */ 480 public void writeMessageSetTo(final CodedOutputStream output) 481 throws IOException { 482 for (final Map.Entry<FieldDescriptorType, Object> entry: 483 fields.entrySet()) { 484 final FieldDescriptorType descriptor = entry.getKey(); 485 if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && 486 !descriptor.isRepeated() && !descriptor.isPacked()) { 487 output.writeMessageSetExtension(entry.getKey().getNumber(), 488 (MessageLite) entry.getValue()); 489 } else { 490 writeField(descriptor, entry.getValue(), output); 491 } 492 } 493 } 494 495 /** 496 * Write a single tag-value pair to the stream. 497 * 498 * @param output The output stream. 499 * @param type The field's type. 500 * @param number The field's number. 501 * @param value Object representing the field's value. Must be of the exact 502 * type which would be returned by 503 * {@link Message#getField(Descriptors.FieldDescriptor)} for 504 * this field. 505 */ 506 private static void writeElement(final CodedOutputStream output, 507 final WireFormat.FieldType type, 508 final int number, 509 final Object value) throws IOException { 510 // Special case for groups, which need a start and end tag; other fields 511 // can just use writeTag() and writeFieldNoTag(). 512 if (type == WireFormat.FieldType.GROUP) { 513 output.writeGroup(number, (MessageLite) value); 514 } else { 515 output.writeTag(number, getWireFormatForFieldType(type, false)); 516 writeElementNoTag(output, type, value); 517 } 518 } 519 520 /** 521 * Write a field of arbitrary type, without its tag, to the stream. 522 * 523 * @param output The output stream. 524 * @param type The field's type. 525 * @param value Object representing the field's value. Must be of the exact 526 * type which would be returned by 527 * {@link Message#getField(Descriptors.FieldDescriptor)} for 528 * this field. 529 */ 530 private static void writeElementNoTag( 531 final CodedOutputStream output, 532 final WireFormat.FieldType type, 533 final Object value) throws IOException { 534 switch (type) { 535 case DOUBLE : output.writeDoubleNoTag ((Double ) value); break; 536 case FLOAT : output.writeFloatNoTag ((Float ) value); break; 537 case INT64 : output.writeInt64NoTag ((Long ) value); break; 538 case UINT64 : output.writeUInt64NoTag ((Long ) value); break; 539 case INT32 : output.writeInt32NoTag ((Integer ) value); break; 540 case FIXED64 : output.writeFixed64NoTag ((Long ) value); break; 541 case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break; 542 case BOOL : output.writeBoolNoTag ((Boolean ) value); break; 543 case STRING : output.writeStringNoTag ((String ) value); break; 544 case GROUP : output.writeGroupNoTag ((MessageLite) value); break; 545 case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break; 546 case BYTES : output.writeBytesNoTag ((ByteString ) value); break; 547 case UINT32 : output.writeUInt32NoTag ((Integer ) value); break; 548 case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break; 549 case SFIXED64: output.writeSFixed64NoTag((Long ) value); break; 550 case SINT32 : output.writeSInt32NoTag ((Integer ) value); break; 551 case SINT64 : output.writeSInt64NoTag ((Long ) value); break; 552 553 case ENUM: 554 output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); 555 break; 556 } 557 } 558 559 /** Write a single field. */ 560 public static void writeField(final FieldDescriptorLite<?> descriptor, 561 final Object value, 562 final CodedOutputStream output) 563 throws IOException { 564 WireFormat.FieldType type = descriptor.getLiteType(); 565 int number = descriptor.getNumber(); 566 if (descriptor.isRepeated()) { 567 final List valueList = (List)value; 568 if (descriptor.isPacked()) { 569 output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED); 570 // Compute the total data size so the length can be written. 571 int dataSize = 0; 572 for (final Object element : valueList) { 573 dataSize += computeElementSizeNoTag(type, element); 574 } 575 output.writeRawVarint32(dataSize); 576 // Write the data itself, without any tags. 577 for (final Object element : valueList) { 578 writeElementNoTag(output, type, element); 579 } 580 } else { 581 for (final Object element : valueList) { 582 writeElement(output, type, number, element); 583 } 584 } 585 } else { 586 writeElement(output, type, number, value); 587 } 588 } 589 590 /** 591 * See {@link Message#getSerializedSize()}. It's up to the caller to cache 592 * the resulting size if desired. 593 */ 594 public int getSerializedSize() { 595 int size = 0; 596 for (final Map.Entry<FieldDescriptorType, Object> entry: 597 fields.entrySet()) { 598 size += computeFieldSize(entry.getKey(), entry.getValue()); 599 } 600 return size; 601 } 602 603 /** 604 * Like {@link #getSerializedSize} but uses MessageSet wire format. 605 */ 606 public int getMessageSetSerializedSize() { 607 int size = 0; 608 for (final Map.Entry<FieldDescriptorType, Object> entry: 609 fields.entrySet()) { 610 final FieldDescriptorType descriptor = entry.getKey(); 611 if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && 612 !descriptor.isRepeated() && !descriptor.isPacked()) { 613 size += CodedOutputStream.computeMessageSetExtensionSize( 614 entry.getKey().getNumber(), (MessageLite) entry.getValue()); 615 } else { 616 size += computeFieldSize(descriptor, entry.getValue()); 617 } 618 } 619 return size; 620 } 621 622 /** 623 * Compute the number of bytes that would be needed to encode a 624 * single tag/value pair of arbitrary type. 625 * 626 * @param type The field's type. 627 * @param number The field's number. 628 * @param value Object representing the field's value. Must be of the exact 629 * type which would be returned by 630 * {@link Message#getField(Descriptors.FieldDescriptor)} for 631 * this field. 632 */ 633 private static int computeElementSize( 634 final WireFormat.FieldType type, 635 final int number, final Object value) { 636 int tagSize = CodedOutputStream.computeTagSize(number); 637 if (type == WireFormat.FieldType.GROUP) { 638 tagSize *= 2; 639 } 640 return tagSize + computeElementSizeNoTag(type, value); 641 } 642 643 /** 644 * Compute the number of bytes that would be needed to encode a 645 * particular value of arbitrary type, excluding tag. 646 * 647 * @param type The field's type. 648 * @param value Object representing the field's value. Must be of the exact 649 * type which would be returned by 650 * {@link Message#getField(Descriptors.FieldDescriptor)} for 651 * this field. 652 */ 653 private static int computeElementSizeNoTag( 654 final WireFormat.FieldType type, final Object value) { 655 switch (type) { 656 // Note: Minor violation of 80-char limit rule here because this would 657 // actually be harder to read if we wrapped the lines. 658 case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value); 659 case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value); 660 case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value); 661 case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value); 662 case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value); 663 case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value); 664 case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value); 665 case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value); 666 case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value); 667 case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value); 668 case MESSAGE : return CodedOutputStream.computeMessageSizeNoTag ((MessageLite)value); 669 case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value); 670 case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value); 671 case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value); 672 case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value); 673 case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value); 674 case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value); 675 676 case ENUM: 677 return CodedOutputStream.computeEnumSizeNoTag( 678 ((Internal.EnumLite) value).getNumber()); 679 } 680 681 throw new RuntimeException( 682 "There is no way to get here, but the compiler thinks otherwise."); 683 } 684 685 /** 686 * Compute the number of bytes needed to encode a particular field. 687 */ 688 public static int computeFieldSize(final FieldDescriptorLite<?> descriptor, 689 final Object value) { 690 WireFormat.FieldType type = descriptor.getLiteType(); 691 int number = descriptor.getNumber(); 692 if (descriptor.isRepeated()) { 693 if (descriptor.isPacked()) { 694 int dataSize = 0; 695 for (final Object element : (List)value) { 696 dataSize += computeElementSizeNoTag(type, element); 697 } 698 return dataSize + 699 CodedOutputStream.computeTagSize(number) + 700 CodedOutputStream.computeRawVarint32Size(dataSize); 701 } else { 702 int size = 0; 703 for (final Object element : (List)value) { 704 size += computeElementSize(type, number, element); 705 } 706 return size; 707 } 708 } else { 709 return computeElementSize(type, number, value); 710 } 711 } 712 } 713