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