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@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> { getNumber()57 int getNumber(); getLiteType()58 WireFormat.FieldType getLiteType(); getLiteJavaType()59 WireFormat.JavaType getLiteJavaType(); isRepeated()60 boolean isRepeated(); isPacked()61 boolean isPacked(); getEnumType()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}. internalMergeFrom( MessageLite.Builder to, MessageLite from)66 MessageLite.Builder internalMergeFrom( 67 MessageLite.Builder to, MessageLite from); 68 } 69 70 private Map<FieldDescriptorType, Object> fields; 71 72 /** Construct a new FieldSet. */ 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 */ FieldSet(final boolean dummy)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>> newFieldSet()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>> emptySet()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") makeImmutable()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()}. */ clear()121 public void clear() { 122 fields.clear(); 123 } 124 125 /** 126 * Get a simple map containing all the fields. 127 */ getAllFields()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 */ iterator()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 */ hasField(final FieldDescriptorType descriptor)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 */ getField(final FieldDescriptorType descriptor)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") setField(final FieldDescriptorType descriptor, Object value)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 */ clearField(final FieldDescriptorType descriptor)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 */ getRepeatedFieldCount(final FieldDescriptorType descriptor)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 */ getRepeatedField(final FieldDescriptorType descriptor, final int index)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") setRepeatedField(final FieldDescriptorType descriptor, final int index, final Object value)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") addRepeatedField(final FieldDescriptorType descriptor, final Object value)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 */ verifyType(final WireFormat.FieldType type, final Object value)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") isInitialized()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 */ getWireFormatForFieldType(final WireFormat.FieldType type, boolean isPacked)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") mergeFrom(final FieldSet<FieldDescriptorType> other)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 */ readPrimitiveField( CodedInputStream input, final WireFormat.FieldType type)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)}. */ writeTo(final CodedOutputStream output)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 */ writeMessageSetTo(final CodedOutputStream output)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 */ writeElement(final CodedOutputStream output, final WireFormat.FieldType type, final int number, final Object value)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 */ writeElementNoTag( final CodedOutputStream output, final WireFormat.FieldType type, final Object value)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. */ writeField(final FieldDescriptorLite<?> descriptor, final Object value, final CodedOutputStream output)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 */ getSerializedSize()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 */ getMessageSetSerializedSize()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 */ computeElementSize( final WireFormat.FieldType type, final int number, final Object value)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 */ computeElementSizeNoTag( final WireFormat.FieldType type, final Object value)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 */ computeFieldSize(final FieldDescriptorLite<?> descriptor, final Object value)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