1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import com.google.protobuf.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@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> { getNumber()58 int getNumber(); getLiteType()59 WireFormat.FieldType getLiteType(); getLiteJavaType()60 WireFormat.JavaType getLiteJavaType(); isRepeated()61 boolean isRepeated(); isPacked()62 boolean isPacked(); getEnumType()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}. internalMergeFrom( MessageLite.Builder to, MessageLite from)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. */ 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 */ FieldSet(final boolean dummy)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>> newFieldSet()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>> emptySet()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") makeImmutable()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 */ isImmutable()120 public boolean isImmutable() { 121 return isImmutable; 122 } 123 124 @Override equals(Object o)125 public boolean equals(Object o) { 126 if (this == o) { 127 return true; 128 } 129 130 if (!(o instanceof FieldSet)) { 131 return false; 132 } 133 134 FieldSet<?> other = (FieldSet<?>) o; 135 return fields.equals(other.fields); 136 } 137 138 @Override hashCode()139 public int hashCode() { 140 return fields.hashCode(); 141 } 142 143 /** 144 * Clones the FieldSet. The returned FieldSet will be mutable even if the 145 * original FieldSet was immutable. 146 * 147 * @return the newly cloned FieldSet 148 */ 149 @Override clone()150 public FieldSet<FieldDescriptorType> clone() { 151 // We can't just call fields.clone because List objects in the map 152 // should not be shared. 153 FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet(); 154 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 155 Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); 156 FieldDescriptorType descriptor = entry.getKey(); 157 clone.setField(descriptor, entry.getValue()); 158 } 159 for (Map.Entry<FieldDescriptorType, Object> entry : 160 fields.getOverflowEntries()) { 161 FieldDescriptorType descriptor = entry.getKey(); 162 clone.setField(descriptor, entry.getValue()); 163 } 164 clone.hasLazyField = hasLazyField; 165 return clone; 166 } 167 168 169 // ================================================================= 170 171 /** See {@link Message.Builder#clear()}. */ clear()172 public void clear() { 173 fields.clear(); 174 hasLazyField = false; 175 } 176 177 /** 178 * Get a simple map containing all the fields. 179 */ getAllFields()180 public Map<FieldDescriptorType, Object> getAllFields() { 181 if (hasLazyField) { 182 SmallSortedMap<FieldDescriptorType, Object> result = 183 SmallSortedMap.newFieldMap(16); 184 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 185 cloneFieldEntry(result, fields.getArrayEntryAt(i)); 186 } 187 for (Map.Entry<FieldDescriptorType, Object> entry : 188 fields.getOverflowEntries()) { 189 cloneFieldEntry(result, entry); 190 } 191 if (fields.isImmutable()) { 192 result.makeImmutable(); 193 } 194 return result; 195 } 196 return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); 197 } 198 cloneFieldEntry(Map<FieldDescriptorType, Object> map, Map.Entry<FieldDescriptorType, Object> entry)199 private void cloneFieldEntry(Map<FieldDescriptorType, Object> map, 200 Map.Entry<FieldDescriptorType, Object> entry) { 201 FieldDescriptorType key = entry.getKey(); 202 Object value = entry.getValue(); 203 if (value instanceof LazyField) { 204 map.put(key, ((LazyField) value).getValue()); 205 } else { 206 map.put(key, value); 207 } 208 } 209 210 /** 211 * Get an iterator to the field map. This iterator should not be leaked out 212 * of the protobuf library as it is not protected from mutation when fields 213 * is not immutable. 214 */ iterator()215 public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { 216 if (hasLazyField) { 217 return new LazyIterator<FieldDescriptorType>( 218 fields.entrySet().iterator()); 219 } 220 return fields.entrySet().iterator(); 221 } 222 223 /** 224 * Useful for implementing 225 * {@link Message#hasField(Descriptors.FieldDescriptor)}. 226 */ hasField(final FieldDescriptorType descriptor)227 public boolean hasField(final FieldDescriptorType descriptor) { 228 if (descriptor.isRepeated()) { 229 throw new IllegalArgumentException( 230 "hasField() can only be called on non-repeated fields."); 231 } 232 233 return fields.get(descriptor) != null; 234 } 235 236 /** 237 * Useful for implementing 238 * {@link Message#getField(Descriptors.FieldDescriptor)}. This method 239 * returns {@code null} if the field is not set; in this case it is up 240 * to the caller to fetch the field's default value. 241 */ getField(final FieldDescriptorType descriptor)242 public Object getField(final FieldDescriptorType descriptor) { 243 Object o = fields.get(descriptor); 244 if (o instanceof LazyField) { 245 return ((LazyField) o).getValue(); 246 } 247 return o; 248 } 249 250 /** 251 * Useful for implementing 252 * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. 253 */ 254 @SuppressWarnings({"unchecked", "rawtypes"}) setField(final FieldDescriptorType descriptor, Object value)255 public void setField(final FieldDescriptorType descriptor, 256 Object value) { 257 if (descriptor.isRepeated()) { 258 if (!(value instanceof List)) { 259 throw new IllegalArgumentException( 260 "Wrong object type used with protocol message reflection."); 261 } 262 263 // Wrap the contents in a new list so that the caller cannot change 264 // the list's contents after setting it. 265 final List newList = new ArrayList(); 266 newList.addAll((List) value); 267 for (final Object element : newList) { 268 verifyType(descriptor.getLiteType(), element); 269 } 270 value = newList; 271 } else { 272 verifyType(descriptor.getLiteType(), value); 273 } 274 275 if (value instanceof LazyField) { 276 hasLazyField = true; 277 } 278 fields.put(descriptor, value); 279 } 280 281 /** 282 * Useful for implementing 283 * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. 284 */ clearField(final FieldDescriptorType descriptor)285 public void clearField(final FieldDescriptorType descriptor) { 286 fields.remove(descriptor); 287 if (fields.isEmpty()) { 288 hasLazyField = false; 289 } 290 } 291 292 /** 293 * Useful for implementing 294 * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. 295 */ getRepeatedFieldCount(final FieldDescriptorType descriptor)296 public int getRepeatedFieldCount(final FieldDescriptorType descriptor) { 297 if (!descriptor.isRepeated()) { 298 throw new IllegalArgumentException( 299 "getRepeatedField() can only be called on repeated fields."); 300 } 301 302 final Object value = getField(descriptor); 303 if (value == null) { 304 return 0; 305 } else { 306 return ((List<?>) value).size(); 307 } 308 } 309 310 /** 311 * Useful for implementing 312 * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. 313 */ getRepeatedField(final FieldDescriptorType descriptor, final int index)314 public Object getRepeatedField(final FieldDescriptorType descriptor, 315 final int index) { 316 if (!descriptor.isRepeated()) { 317 throw new IllegalArgumentException( 318 "getRepeatedField() can only be called on repeated fields."); 319 } 320 321 final Object value = getField(descriptor); 322 323 if (value == null) { 324 throw new IndexOutOfBoundsException(); 325 } else { 326 return ((List<?>) value).get(index); 327 } 328 } 329 330 /** 331 * Useful for implementing 332 * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. 333 */ 334 @SuppressWarnings("unchecked") setRepeatedField(final FieldDescriptorType descriptor, final int index, final Object value)335 public void setRepeatedField(final FieldDescriptorType descriptor, 336 final int index, 337 final Object value) { 338 if (!descriptor.isRepeated()) { 339 throw new IllegalArgumentException( 340 "getRepeatedField() can only be called on repeated fields."); 341 } 342 343 final Object list = getField(descriptor); 344 if (list == null) { 345 throw new IndexOutOfBoundsException(); 346 } 347 348 verifyType(descriptor.getLiteType(), value); 349 ((List<Object>) list).set(index, value); 350 } 351 352 /** 353 * Useful for implementing 354 * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. 355 */ 356 @SuppressWarnings("unchecked") addRepeatedField(final FieldDescriptorType descriptor, final Object value)357 public void addRepeatedField(final FieldDescriptorType descriptor, 358 final Object value) { 359 if (!descriptor.isRepeated()) { 360 throw new IllegalArgumentException( 361 "addRepeatedField() can only be called on repeated fields."); 362 } 363 364 verifyType(descriptor.getLiteType(), value); 365 366 final Object existingValue = getField(descriptor); 367 List<Object> list; 368 if (existingValue == null) { 369 list = new ArrayList<Object>(); 370 fields.put(descriptor, list); 371 } else { 372 list = (List<Object>) existingValue; 373 } 374 375 list.add(value); 376 } 377 378 /** 379 * Verifies that the given object is of the correct type to be a valid 380 * value for the given field. (For repeated fields, this checks if the 381 * object is the right type to be one element of the field.) 382 * 383 * @throws IllegalArgumentException The value is not of the right type. 384 */ verifyType(final WireFormat.FieldType type, final Object value)385 private static void verifyType(final WireFormat.FieldType type, 386 final Object value) { 387 if (value == null) { 388 throw new NullPointerException(); 389 } 390 391 boolean isValid = false; 392 switch (type.getJavaType()) { 393 case INT: isValid = value instanceof Integer ; break; 394 case LONG: isValid = value instanceof Long ; break; 395 case FLOAT: isValid = value instanceof Float ; break; 396 case DOUBLE: isValid = value instanceof Double ; break; 397 case BOOLEAN: isValid = value instanceof Boolean ; break; 398 case STRING: isValid = value instanceof String ; break; 399 case BYTE_STRING: 400 isValid = value instanceof ByteString || value instanceof byte[]; 401 break; 402 case ENUM: 403 // TODO(kenton): Caller must do type checking here, I guess. 404 isValid = 405 (value instanceof Integer || value instanceof Internal.EnumLite); 406 break; 407 case MESSAGE: 408 // TODO(kenton): Caller must do type checking here, I guess. 409 isValid = 410 (value instanceof MessageLite) || (value instanceof LazyField); 411 break; 412 } 413 414 if (!isValid) { 415 // TODO(kenton): When chaining calls to setField(), it can be hard to 416 // tell from the stack trace which exact call failed, since the whole 417 // chain is considered one line of code. It would be nice to print 418 // more information here, e.g. naming the field. We used to do that. 419 // But we can't now that FieldSet doesn't use descriptors. Maybe this 420 // isn't a big deal, though, since it would only really apply when using 421 // reflection and generally people don't chain reflection setters. 422 throw new IllegalArgumentException( 423 "Wrong object type used with protocol message reflection."); 424 } 425 } 426 427 // ================================================================= 428 // Parsing and serialization 429 430 /** 431 * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} 432 * itself does not have any way of knowing about required fields that 433 * aren't actually present in the set, it is up to the caller to check 434 * that all required fields are present. 435 */ isInitialized()436 public boolean isInitialized() { 437 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 438 if (!isInitialized(fields.getArrayEntryAt(i))) { 439 return false; 440 } 441 } 442 for (final Map.Entry<FieldDescriptorType, Object> entry : 443 fields.getOverflowEntries()) { 444 if (!isInitialized(entry)) { 445 return false; 446 } 447 } 448 return true; 449 } 450 451 @SuppressWarnings("unchecked") isInitialized( final Map.Entry<FieldDescriptorType, Object> entry)452 private boolean isInitialized( 453 final Map.Entry<FieldDescriptorType, Object> entry) { 454 final FieldDescriptorType descriptor = entry.getKey(); 455 if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { 456 if (descriptor.isRepeated()) { 457 for (final MessageLite element: 458 (List<MessageLite>) entry.getValue()) { 459 if (!element.isInitialized()) { 460 return false; 461 } 462 } 463 } else { 464 Object value = entry.getValue(); 465 if (value instanceof MessageLite) { 466 if (!((MessageLite) value).isInitialized()) { 467 return false; 468 } 469 } else if (value instanceof LazyField) { 470 return true; 471 } else { 472 throw new IllegalArgumentException( 473 "Wrong object type used with protocol message reflection."); 474 } 475 } 476 } 477 return true; 478 } 479 480 /** 481 * Given a field type, return the wire type. 482 * 483 * @returns One of the {@code WIRETYPE_} constants defined in 484 * {@link WireFormat}. 485 */ getWireFormatForFieldType(final WireFormat.FieldType type, boolean isPacked)486 static int getWireFormatForFieldType(final WireFormat.FieldType type, 487 boolean isPacked) { 488 if (isPacked) { 489 return WireFormat.WIRETYPE_LENGTH_DELIMITED; 490 } else { 491 return type.getWireType(); 492 } 493 } 494 495 /** 496 * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another 497 * {@link FieldSet}. 498 */ mergeFrom(final FieldSet<FieldDescriptorType> other)499 public void mergeFrom(final FieldSet<FieldDescriptorType> other) { 500 for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { 501 mergeFromField(other.fields.getArrayEntryAt(i)); 502 } 503 for (final Map.Entry<FieldDescriptorType, Object> entry : 504 other.fields.getOverflowEntries()) { 505 mergeFromField(entry); 506 } 507 } 508 cloneIfMutable(Object value)509 private Object cloneIfMutable(Object value) { 510 if (value instanceof byte[]) { 511 byte[] bytes = (byte[]) value; 512 byte[] copy = new byte[bytes.length]; 513 System.arraycopy(bytes, 0, copy, 0, bytes.length); 514 return copy; 515 } else { 516 return value; 517 } 518 } 519 520 @SuppressWarnings({"unchecked", "rawtypes"}) mergeFromField( final Map.Entry<FieldDescriptorType, Object> entry)521 private void mergeFromField( 522 final Map.Entry<FieldDescriptorType, Object> entry) { 523 final FieldDescriptorType descriptor = entry.getKey(); 524 Object otherValue = entry.getValue(); 525 if (otherValue instanceof LazyField) { 526 otherValue = ((LazyField) otherValue).getValue(); 527 } 528 529 if (descriptor.isRepeated()) { 530 Object value = getField(descriptor); 531 if (value == null) { 532 value = new ArrayList(); 533 } 534 for (Object element : (List) otherValue) { 535 ((List) value).add(cloneIfMutable(element)); 536 } 537 fields.put(descriptor, value); 538 } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { 539 Object value = getField(descriptor); 540 if (value == null) { 541 fields.put(descriptor, cloneIfMutable(otherValue)); 542 } else { 543 // Merge the messages. 544 value = descriptor.internalMergeFrom( 545 ((MessageLite) value).toBuilder(), (MessageLite) otherValue) 546 .build(); 547 548 fields.put(descriptor, value); 549 } 550 } else { 551 fields.put(descriptor, cloneIfMutable(otherValue)); 552 } 553 } 554 555 // TODO(kenton): Move static parsing and serialization methods into some 556 // other class. Probably WireFormat. 557 558 /** 559 * Read a field of any primitive type for immutable messages from a 560 * CodedInputStream. Enums, groups, and embedded messages are not handled by 561 * this method. 562 * 563 * @param input The stream from which to read. 564 * @param type Declared type of the field. 565 * @param checkUtf8 When true, check that the input is valid utf8. 566 * @return An object representing the field's value, of the exact 567 * type which would be returned by 568 * {@link Message#getField(Descriptors.FieldDescriptor)} for 569 * this field. 570 */ readPrimitiveField( CodedInputStream input, final WireFormat.FieldType type, boolean checkUtf8)571 public static Object readPrimitiveField( 572 CodedInputStream input, 573 final WireFormat.FieldType type, 574 boolean checkUtf8) throws IOException { 575 if (checkUtf8) { 576 return WireFormat.readPrimitiveField(input, type, 577 WireFormat.Utf8Validation.STRICT); 578 } else { 579 return WireFormat.readPrimitiveField(input, type, 580 WireFormat.Utf8Validation.LOOSE); 581 } 582 } 583 584 585 /** See {@link Message#writeTo(CodedOutputStream)}. */ writeTo(final CodedOutputStream output)586 public void writeTo(final CodedOutputStream output) 587 throws IOException { 588 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 589 final Map.Entry<FieldDescriptorType, Object> entry = 590 fields.getArrayEntryAt(i); 591 writeField(entry.getKey(), entry.getValue(), output); 592 } 593 for (final Map.Entry<FieldDescriptorType, Object> entry : 594 fields.getOverflowEntries()) { 595 writeField(entry.getKey(), entry.getValue(), output); 596 } 597 } 598 599 /** 600 * Like {@link #writeTo} but uses MessageSet wire format. 601 */ writeMessageSetTo(final CodedOutputStream output)602 public void writeMessageSetTo(final CodedOutputStream output) 603 throws IOException { 604 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 605 writeMessageSetTo(fields.getArrayEntryAt(i), output); 606 } 607 for (final Map.Entry<FieldDescriptorType, Object> entry : 608 fields.getOverflowEntries()) { 609 writeMessageSetTo(entry, output); 610 } 611 } 612 writeMessageSetTo( final Map.Entry<FieldDescriptorType, Object> entry, final CodedOutputStream output)613 private void writeMessageSetTo( 614 final Map.Entry<FieldDescriptorType, Object> entry, 615 final CodedOutputStream output) throws IOException { 616 final FieldDescriptorType descriptor = entry.getKey(); 617 if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && 618 !descriptor.isRepeated() && !descriptor.isPacked()) { 619 Object value = entry.getValue(); 620 if (value instanceof LazyField) { 621 value = ((LazyField) value).getValue(); 622 } 623 output.writeMessageSetExtension(entry.getKey().getNumber(), 624 (MessageLite) value); 625 } else { 626 writeField(descriptor, entry.getValue(), output); 627 } 628 } 629 630 /** 631 * Write a single tag-value pair to the stream. 632 * 633 * @param output The output stream. 634 * @param type The field's type. 635 * @param number The field's number. 636 * @param value Object representing the field's value. Must be of the exact 637 * type which would be returned by 638 * {@link Message#getField(Descriptors.FieldDescriptor)} for 639 * this field. 640 */ writeElement( final CodedOutputStream output, final WireFormat.FieldType type, final int number, final Object value)641 static void writeElement( 642 final CodedOutputStream output, 643 final WireFormat.FieldType type, 644 final int number, 645 final Object value) throws IOException { 646 // Special case for groups, which need a start and end tag; other fields 647 // can just use writeTag() and writeFieldNoTag(). 648 if (type == WireFormat.FieldType.GROUP) { 649 output.writeGroup(number, (MessageLite) value); 650 } else { 651 output.writeTag(number, getWireFormatForFieldType(type, false)); 652 writeElementNoTag(output, type, value); 653 } 654 } 655 656 /** 657 * Write a field of arbitrary type, without its tag, to the stream. 658 * 659 * @param output The output stream. 660 * @param type The field's type. 661 * @param value Object representing the field's value. Must be of the exact 662 * type which would be returned by 663 * {@link Message#getField(Descriptors.FieldDescriptor)} for 664 * this field. 665 */ writeElementNoTag( final CodedOutputStream output, final WireFormat.FieldType type, final Object value)666 static void writeElementNoTag( 667 final CodedOutputStream output, 668 final WireFormat.FieldType type, 669 final Object value) throws IOException { 670 switch (type) { 671 case DOUBLE : output.writeDoubleNoTag ((Double ) value); break; 672 case FLOAT : output.writeFloatNoTag ((Float ) value); break; 673 case INT64 : output.writeInt64NoTag ((Long ) value); break; 674 case UINT64 : output.writeUInt64NoTag ((Long ) value); break; 675 case INT32 : output.writeInt32NoTag ((Integer ) value); break; 676 case FIXED64 : output.writeFixed64NoTag ((Long ) value); break; 677 case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break; 678 case BOOL : output.writeBoolNoTag ((Boolean ) value); break; 679 case GROUP : output.writeGroupNoTag ((MessageLite) value); break; 680 case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break; 681 case STRING: 682 if (value instanceof ByteString) { 683 output.writeBytesNoTag((ByteString) value); 684 } else { 685 output.writeStringNoTag((String) value); 686 } 687 break; 688 case BYTES: 689 if (value instanceof ByteString) { 690 output.writeBytesNoTag((ByteString) value); 691 } else { 692 output.writeByteArrayNoTag((byte[]) value); 693 } 694 break; 695 case UINT32 : output.writeUInt32NoTag ((Integer ) value); break; 696 case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break; 697 case SFIXED64: output.writeSFixed64NoTag((Long ) value); break; 698 case SINT32 : output.writeSInt32NoTag ((Integer ) value); break; 699 case SINT64 : output.writeSInt64NoTag ((Long ) value); break; 700 701 case ENUM: 702 if (value instanceof Internal.EnumLite) { 703 output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); 704 } else { 705 output.writeEnumNoTag(((Integer) value).intValue()); 706 } 707 break; 708 } 709 } 710 711 /** Write a single field. */ writeField(final FieldDescriptorLite<?> descriptor, final Object value, final CodedOutputStream output)712 public static void writeField(final FieldDescriptorLite<?> descriptor, 713 final Object value, 714 final CodedOutputStream output) 715 throws IOException { 716 WireFormat.FieldType type = descriptor.getLiteType(); 717 int number = descriptor.getNumber(); 718 if (descriptor.isRepeated()) { 719 final List<?> valueList = (List<?>)value; 720 if (descriptor.isPacked()) { 721 output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED); 722 // Compute the total data size so the length can be written. 723 int dataSize = 0; 724 for (final Object element : valueList) { 725 dataSize += computeElementSizeNoTag(type, element); 726 } 727 output.writeRawVarint32(dataSize); 728 // Write the data itself, without any tags. 729 for (final Object element : valueList) { 730 writeElementNoTag(output, type, element); 731 } 732 } else { 733 for (final Object element : valueList) { 734 writeElement(output, type, number, element); 735 } 736 } 737 } else { 738 if (value instanceof LazyField) { 739 writeElement(output, type, number, ((LazyField) value).getValue()); 740 } else { 741 writeElement(output, type, number, value); 742 } 743 } 744 } 745 746 /** 747 * See {@link Message#getSerializedSize()}. It's up to the caller to cache 748 * the resulting size if desired. 749 */ getSerializedSize()750 public int getSerializedSize() { 751 int size = 0; 752 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 753 final Map.Entry<FieldDescriptorType, Object> entry = 754 fields.getArrayEntryAt(i); 755 size += computeFieldSize(entry.getKey(), entry.getValue()); 756 } 757 for (final Map.Entry<FieldDescriptorType, Object> entry : 758 fields.getOverflowEntries()) { 759 size += computeFieldSize(entry.getKey(), entry.getValue()); 760 } 761 return size; 762 } 763 764 /** 765 * Like {@link #getSerializedSize} but uses MessageSet wire format. 766 */ getMessageSetSerializedSize()767 public int getMessageSetSerializedSize() { 768 int size = 0; 769 for (int i = 0; i < fields.getNumArrayEntries(); i++) { 770 size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); 771 } 772 for (final Map.Entry<FieldDescriptorType, Object> entry : 773 fields.getOverflowEntries()) { 774 size += getMessageSetSerializedSize(entry); 775 } 776 return size; 777 } 778 getMessageSetSerializedSize( final Map.Entry<FieldDescriptorType, Object> entry)779 private int getMessageSetSerializedSize( 780 final Map.Entry<FieldDescriptorType, Object> entry) { 781 final FieldDescriptorType descriptor = entry.getKey(); 782 Object value = entry.getValue(); 783 if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE 784 && !descriptor.isRepeated() && !descriptor.isPacked()) { 785 if (value instanceof LazyField) { 786 return CodedOutputStream.computeLazyFieldMessageSetExtensionSize( 787 entry.getKey().getNumber(), (LazyField) value); 788 } else { 789 return CodedOutputStream.computeMessageSetExtensionSize( 790 entry.getKey().getNumber(), (MessageLite) value); 791 } 792 } else { 793 return computeFieldSize(descriptor, value); 794 } 795 } 796 797 /** 798 * Compute the number of bytes that would be needed to encode a 799 * single tag/value pair of arbitrary type. 800 * 801 * @param type The field's type. 802 * @param number The field's number. 803 * @param value Object representing the field's value. Must be of the exact 804 * type which would be returned by 805 * {@link Message#getField(Descriptors.FieldDescriptor)} for 806 * this field. 807 */ computeElementSize( final WireFormat.FieldType type, final int number, final Object value)808 static int computeElementSize( 809 final WireFormat.FieldType type, final int number, final Object value) { 810 int tagSize = CodedOutputStream.computeTagSize(number); 811 if (type == WireFormat.FieldType.GROUP) { 812 // Only count the end group tag for proto2 messages as for proto1 the end 813 // group tag will be counted as a part of getSerializedSize(). 814 tagSize *= 2; 815 } 816 return tagSize + computeElementSizeNoTag(type, value); 817 } 818 819 /** 820 * Compute the number of bytes that would be needed to encode a 821 * particular value of arbitrary type, excluding tag. 822 * 823 * @param type The field's type. 824 * @param value Object representing the field's value. Must be of the exact 825 * type which would be returned by 826 * {@link Message#getField(Descriptors.FieldDescriptor)} for 827 * this field. 828 */ computeElementSizeNoTag( final WireFormat.FieldType type, final Object value)829 static int computeElementSizeNoTag( 830 final WireFormat.FieldType type, final Object value) { 831 switch (type) { 832 // Note: Minor violation of 80-char limit rule here because this would 833 // actually be harder to read if we wrapped the lines. 834 case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value); 835 case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value); 836 case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value); 837 case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value); 838 case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value); 839 case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value); 840 case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value); 841 case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value); 842 case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value); 843 case BYTES : 844 if (value instanceof ByteString) { 845 return CodedOutputStream.computeBytesSizeNoTag((ByteString) value); 846 } else { 847 return CodedOutputStream.computeByteArraySizeNoTag((byte[]) value); 848 } 849 case STRING : 850 if (value instanceof ByteString) { 851 return CodedOutputStream.computeBytesSizeNoTag((ByteString) value); 852 } else { 853 return CodedOutputStream.computeStringSizeNoTag((String) value); 854 } 855 case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value); 856 case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value); 857 case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value); 858 case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value); 859 case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value); 860 861 case MESSAGE: 862 if (value instanceof LazyField) { 863 return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value); 864 } else { 865 return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value); 866 } 867 868 case ENUM: 869 if (value instanceof Internal.EnumLite) { 870 return CodedOutputStream.computeEnumSizeNoTag( 871 ((Internal.EnumLite) value).getNumber()); 872 } else { 873 return CodedOutputStream.computeEnumSizeNoTag((Integer) value); 874 } 875 } 876 877 throw new RuntimeException( 878 "There is no way to get here, but the compiler thinks otherwise."); 879 } 880 881 /** 882 * Compute the number of bytes needed to encode a particular field. 883 */ computeFieldSize(final FieldDescriptorLite<?> descriptor, final Object value)884 public static int computeFieldSize(final FieldDescriptorLite<?> descriptor, 885 final Object value) { 886 WireFormat.FieldType type = descriptor.getLiteType(); 887 int number = descriptor.getNumber(); 888 if (descriptor.isRepeated()) { 889 if (descriptor.isPacked()) { 890 int dataSize = 0; 891 for (final Object element : (List<?>)value) { 892 dataSize += computeElementSizeNoTag(type, element); 893 } 894 return dataSize + 895 CodedOutputStream.computeTagSize(number) + 896 CodedOutputStream.computeRawVarint32Size(dataSize); 897 } else { 898 int size = 0; 899 for (final Object element : (List<?>)value) { 900 size += computeElementSize(type, number, element); 901 } 902 return size; 903 } 904 } else { 905 return computeElementSize(type, number, value); 906 } 907 } 908 } 909