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