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