// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.protobuf; import static com.google.protobuf.Internal.checkNotNull; import com.google.protobuf.LazyField.LazyIterator; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * A class which represents an arbitrary set of fields of some message type. This is used to * implement {@link DynamicMessage}, and also to represent extensions in {@link GeneratedMessage}. * This class is package-private, since outside users should probably be using {@link * DynamicMessage}. * * @author kenton@google.com Kenton Varda */ final class FieldSet> { /** * Interface for a FieldDescriptor or lite extension descriptor. This prevents FieldSet from * depending on {@link Descriptors.FieldDescriptor}. */ public interface FieldDescriptorLite> extends Comparable { int getNumber(); WireFormat.FieldType getLiteType(); WireFormat.JavaType getLiteJavaType(); boolean isRepeated(); boolean isPacked(); Internal.EnumLiteMap getEnumType(); // If getLiteJavaType() == MESSAGE, this merges a message object of the // type into a builder of the type. Returns {@code to}. MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from); } private static final int DEFAULT_FIELD_MAP_ARRAY_SIZE = 16; private final SmallSortedMap fields; private boolean isImmutable; private boolean hasLazyField; /** Construct a new FieldSet. */ private FieldSet() { this.fields = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE); } /** Construct an empty FieldSet. This is only used to initialize DEFAULT_INSTANCE. */ @SuppressWarnings("unused") private FieldSet(final boolean dummy) { this(SmallSortedMap.newFieldMap(0)); makeImmutable(); } private FieldSet(SmallSortedMap fields) { this.fields = fields; makeImmutable(); } /** Construct a new FieldSet. */ public static > FieldSet newFieldSet() { return new FieldSet(); } /** Get an immutable empty FieldSet. */ @SuppressWarnings("unchecked") public static > FieldSet emptySet() { return DEFAULT_INSTANCE; } /** Construct a new Builder. */ public static > Builder newBuilder() { return new Builder(); } @SuppressWarnings("rawtypes") private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); /** Returns {@code true} if empty, {@code false} otherwise. */ boolean isEmpty() { return fields.isEmpty(); } /** Make this FieldSet immutable from this point forward. */ public void makeImmutable() { if (isImmutable) { return; } for (int i = 0; i < fields.getNumArrayEntries(); ++i) { Entry entry = fields.getArrayEntryAt(i); if (entry.getValue() instanceof GeneratedMessageLite) { ((GeneratedMessageLite) entry.getValue()).makeImmutable(); } } fields.makeImmutable(); isImmutable = true; } /** * Returns whether the FieldSet is immutable. This is true if it is the {@link #emptySet} or if * {@link #makeImmutable} were called. * * @return whether the FieldSet is immutable. */ public boolean isImmutable() { return isImmutable; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof FieldSet)) { return false; } FieldSet other = (FieldSet) o; return fields.equals(other.fields); } @Override public int hashCode() { return fields.hashCode(); } /** * Clones the FieldSet. The returned FieldSet will be mutable even if the original FieldSet was * immutable. * * @return the newly cloned FieldSet */ @Override public FieldSet clone() { // We can't just call fields.clone because List objects in the map // should not be shared. FieldSet clone = FieldSet.newFieldSet(); for (int i = 0; i < fields.getNumArrayEntries(); i++) { Map.Entry entry = fields.getArrayEntryAt(i); clone.setField(entry.getKey(), entry.getValue()); } for (Map.Entry entry : fields.getOverflowEntries()) { clone.setField(entry.getKey(), entry.getValue()); } clone.hasLazyField = hasLazyField; return clone; } // ================================================================= /** See {@link Message.Builder#clear()}. */ public void clear() { fields.clear(); hasLazyField = false; } /** Get a simple map containing all the fields. */ public Map getAllFields() { if (hasLazyField) { SmallSortedMap result = cloneAllFieldsMap(fields, /* copyList */ false); if (fields.isImmutable()) { result.makeImmutable(); } return result; } return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); } private static > SmallSortedMap cloneAllFieldsMap( SmallSortedMap fields, boolean copyList) { SmallSortedMap result = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE); for (int i = 0; i < fields.getNumArrayEntries(); i++) { cloneFieldEntry(result, fields.getArrayEntryAt(i), copyList); } for (Map.Entry entry : fields.getOverflowEntries()) { cloneFieldEntry(result, entry, copyList); } return result; } private static > void cloneFieldEntry( Map map, Map.Entry entry, boolean copyList) { T key = entry.getKey(); Object value = entry.getValue(); if (value instanceof LazyField) { map.put(key, ((LazyField) value).getValue()); } else if (copyList && value instanceof List) { map.put(key, new ArrayList<>((List) value)); } else { map.put(key, value); } } /** * Get an iterator to the field map. This iterator should not be leaked out of the protobuf * library as it is not protected from mutation when fields is not immutable. */ public Iterator> iterator() { if (hasLazyField) { return new LazyIterator(fields.entrySet().iterator()); } return fields.entrySet().iterator(); } /** * Get an iterator over the fields in the map in descending (i.e. reverse) order. This iterator * should not be leaked out of the protobuf library as it is not protected from mutation when * fields is not immutable. */ Iterator> descendingIterator() { if (hasLazyField) { return new LazyIterator(fields.descendingEntrySet().iterator()); } return fields.descendingEntrySet().iterator(); } /** Useful for implementing {@link Message#hasField(Descriptors.FieldDescriptor)}. */ public boolean hasField(final T descriptor) { if (descriptor.isRepeated()) { throw new IllegalArgumentException("hasField() can only be called on non-repeated fields."); } return fields.get(descriptor) != null; } /** * Useful for implementing {@link Message#getField(Descriptors.FieldDescriptor)}. This method * returns {@code null} if the field is not set; in this case it is up to the caller to fetch the * field's default value. */ public Object getField(final T descriptor) { Object o = fields.get(descriptor); if (o instanceof LazyField) { return ((LazyField) o).getValue(); } return o; } /** * Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. */ @SuppressWarnings({"unchecked", "rawtypes"}) public void setField(final T descriptor, Object value) { if (descriptor.isRepeated()) { if (!(value instanceof List)) { throw new IllegalArgumentException( "Wrong object type used with protocol message reflection."); } // Wrap the contents in a new list so that the caller cannot change // the list's contents after setting it. final List newList = new ArrayList<>(); newList.addAll((List) value); for (final Object element : newList) { verifyType(descriptor, element); } value = newList; } else { verifyType(descriptor, value); } if (value instanceof LazyField) { hasLazyField = true; } fields.put(descriptor, value); } /** Useful for implementing {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ public void clearField(final T descriptor) { fields.remove(descriptor); if (fields.isEmpty()) { hasLazyField = false; } } /** Useful for implementing {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */ public int getRepeatedFieldCount(final T descriptor) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedField() can only be called on repeated fields."); } final Object value = getField(descriptor); if (value == null) { return 0; } else { return ((List) value).size(); } } /** Useful for implementing {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */ public Object getRepeatedField(final T descriptor, final int index) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedField() can only be called on repeated fields."); } final Object value = getField(descriptor); if (value == null) { throw new IndexOutOfBoundsException(); } else { return ((List) value).get(index); } } /** * Useful for implementing {@link * Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. */ @SuppressWarnings("unchecked") public void setRepeatedField(final T descriptor, final int index, final Object value) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedField() can only be called on repeated fields."); } final Object list = getField(descriptor); if (list == null) { throw new IndexOutOfBoundsException(); } verifyType(descriptor, value); ((List) list).set(index, value); } /** * Useful for implementing {@link * Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. */ @SuppressWarnings("unchecked") public void addRepeatedField(final T descriptor, final Object value) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "addRepeatedField() can only be called on repeated fields."); } verifyType(descriptor, value); final Object existingValue = getField(descriptor); List list; if (existingValue == null) { list = new ArrayList(); fields.put(descriptor, list); } else { list = (List) existingValue; } list.add(value); } /** * Verifies that the given object is of the correct type to be a valid value for the given field. * (For repeated fields, this checks if the object is the right type to be one element of the * field.) * * @throws IllegalArgumentException the value is not of the right type */ private void verifyType(final T descriptor, final Object value) { if (!isValidType(descriptor.getLiteType(), value)) { throw new IllegalArgumentException( String.format( "Wrong object type used with protocol message reflection.\n" + "Field number: %d, field java type: %s, value type: %s\n", descriptor.getNumber(), descriptor.getLiteType().getJavaType(), value.getClass().getName())); } } private static boolean isValidType(final WireFormat.FieldType type, final Object value) { checkNotNull(value); switch (type.getJavaType()) { case INT: return value instanceof Integer; case LONG: return value instanceof Long; case FLOAT: return value instanceof Float; case DOUBLE: return value instanceof Double; case BOOLEAN: return value instanceof Boolean; case STRING: return value instanceof String; case BYTE_STRING: return value instanceof ByteString || value instanceof byte[]; case ENUM: return (value instanceof Integer || value instanceof Internal.EnumLite); case MESSAGE: return (value instanceof MessageLite) || (value instanceof LazyField); } return false; } // ================================================================= // Parsing and serialization /** * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} itself does not have any way * of knowing about required fields that aren't actually present in the set, it is up to the * caller to check that all required fields are present. */ public boolean isInitialized() { for (int i = 0; i < fields.getNumArrayEntries(); i++) { if (!isInitialized(fields.getArrayEntryAt(i))) { return false; } } for (final Map.Entry entry : fields.getOverflowEntries()) { if (!isInitialized(entry)) { return false; } } return true; } private static > boolean isInitialized( final Map.Entry entry) { final T descriptor = entry.getKey(); if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { if (descriptor.isRepeated()) { for (final Object element : (List) entry.getValue()) { if (!isMessageFieldValueInitialized(element)) { return false; } } } else { return isMessageFieldValueInitialized(entry.getValue()); } } return true; } private static boolean isMessageFieldValueInitialized(Object value) { if (value instanceof MessageLiteOrBuilder) { // Message fields cannot have builder values in FieldSet, but can in FieldSet.Builder, and // this method is used by FieldSet.Builder.isInitialized. return ((MessageLiteOrBuilder) value).isInitialized(); } else if (value instanceof LazyField) { return true; } else { throw new IllegalArgumentException( "Wrong object type used with protocol message reflection."); } } /** * Given a field type, return the wire type. * * @return One of the {@code WIRETYPE_} constants defined in {@link WireFormat}. */ static int getWireFormatForFieldType(final WireFormat.FieldType type, boolean isPacked) { if (isPacked) { return WireFormat.WIRETYPE_LENGTH_DELIMITED; } else { return type.getWireType(); } } /** Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}. */ public void mergeFrom(final FieldSet other) { for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { mergeFromField(other.fields.getArrayEntryAt(i)); } for (final Map.Entry entry : other.fields.getOverflowEntries()) { mergeFromField(entry); } } private static Object cloneIfMutable(Object value) { if (value instanceof byte[]) { byte[] bytes = (byte[]) value; byte[] copy = new byte[bytes.length]; System.arraycopy(bytes, 0, copy, 0, bytes.length); return copy; } else { return value; } } @SuppressWarnings({"unchecked", "rawtypes"}) private void mergeFromField(final Map.Entry entry) { final T descriptor = entry.getKey(); Object otherValue = entry.getValue(); if (otherValue instanceof LazyField) { otherValue = ((LazyField) otherValue).getValue(); } if (descriptor.isRepeated()) { Object value = getField(descriptor); if (value == null) { value = new ArrayList<>(); } for (Object element : (List) otherValue) { ((List) value).add(cloneIfMutable(element)); } fields.put(descriptor, value); } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { Object value = getField(descriptor); if (value == null) { fields.put(descriptor, cloneIfMutable(otherValue)); } else { // Merge the messages. value = descriptor .internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue) .build(); fields.put(descriptor, value); } } else { fields.put(descriptor, cloneIfMutable(otherValue)); } } /** * Read a field of any primitive type for immutable messages from a CodedInputStream. Enums, * groups, and embedded messages are not handled by this method. * * @param input the stream from which to read * @param type declared type of the field * @param checkUtf8 When true, check that the input is valid UTF-8 * @return an object representing the field's value, of the exact type which would be returned by * {@link Message#getField(Descriptors.FieldDescriptor)} for this field */ public static Object readPrimitiveField( CodedInputStream input, final WireFormat.FieldType type, boolean checkUtf8) throws IOException { if (checkUtf8) { return WireFormat.readPrimitiveField(input, type, WireFormat.Utf8Validation.STRICT); } else { return WireFormat.readPrimitiveField(input, type, WireFormat.Utf8Validation.LOOSE); } } /** See {@link Message#writeTo(CodedOutputStream)}. */ public void writeTo(final CodedOutputStream output) throws IOException { for (int i = 0; i < fields.getNumArrayEntries(); i++) { final Map.Entry entry = fields.getArrayEntryAt(i); writeField(entry.getKey(), entry.getValue(), output); } for (final Map.Entry entry : fields.getOverflowEntries()) { writeField(entry.getKey(), entry.getValue(), output); } } /** Like {@link #writeTo} but uses MessageSet wire format. */ public void writeMessageSetTo(final CodedOutputStream output) throws IOException { for (int i = 0; i < fields.getNumArrayEntries(); i++) { writeMessageSetTo(fields.getArrayEntryAt(i), output); } for (final Map.Entry entry : fields.getOverflowEntries()) { writeMessageSetTo(entry, output); } } private void writeMessageSetTo(final Map.Entry entry, final CodedOutputStream output) throws IOException { final T descriptor = entry.getKey(); if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && !descriptor.isRepeated() && !descriptor.isPacked()) { Object value = entry.getValue(); if (value instanceof LazyField) { value = ((LazyField) value).getValue(); } output.writeMessageSetExtension(entry.getKey().getNumber(), (MessageLite) value); } else { writeField(descriptor, entry.getValue(), output); } } /** * Write a single tag-value pair to the stream. * * @param output The output stream. * @param type The field's type. * @param number The field's number. * @param value Object representing the field's value. Must be of the exact type which would be * returned by {@link Message#getField(Descriptors.FieldDescriptor)} for this field. */ static void writeElement( final CodedOutputStream output, final WireFormat.FieldType type, final int number, final Object value) throws IOException { // Special case for groups, which need a start and end tag; other fields // can just use writeTag() and writeFieldNoTag(). if (type == WireFormat.FieldType.GROUP) { output.writeGroup(number, (MessageLite) value); } else { output.writeTag(number, getWireFormatForFieldType(type, false)); writeElementNoTag(output, type, value); } } /** * Write a field of arbitrary type, without its tag, to the stream. * * @param output The output stream. * @param type The field's type. * @param value Object representing the field's value. Must be of the exact type which would be * returned by {@link Message#getField(Descriptors.FieldDescriptor)} for this field. */ static void writeElementNoTag( final CodedOutputStream output, final WireFormat.FieldType type, final Object value) throws IOException { switch (type) { case DOUBLE: output.writeDoubleNoTag((Double) value); break; case FLOAT: output.writeFloatNoTag((Float) value); break; case INT64: output.writeInt64NoTag((Long) value); break; case UINT64: output.writeUInt64NoTag((Long) value); break; case INT32: output.writeInt32NoTag((Integer) value); break; case FIXED64: output.writeFixed64NoTag((Long) value); break; case FIXED32: output.writeFixed32NoTag((Integer) value); break; case BOOL: output.writeBoolNoTag((Boolean) value); break; case GROUP: output.writeGroupNoTag((MessageLite) value); break; case MESSAGE: output.writeMessageNoTag((MessageLite) value); break; case STRING: if (value instanceof ByteString) { output.writeBytesNoTag((ByteString) value); } else { output.writeStringNoTag((String) value); } break; case BYTES: if (value instanceof ByteString) { output.writeBytesNoTag((ByteString) value); } else { output.writeByteArrayNoTag((byte[]) value); } break; case UINT32: output.writeUInt32NoTag((Integer) value); break; case SFIXED32: output.writeSFixed32NoTag((Integer) value); break; case SFIXED64: output.writeSFixed64NoTag((Long) value); break; case SINT32: output.writeSInt32NoTag((Integer) value); break; case SINT64: output.writeSInt64NoTag((Long) value); break; case ENUM: if (value instanceof Internal.EnumLite) { output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); } else { output.writeEnumNoTag(((Integer) value).intValue()); } break; } } /** Write a single field. */ public static void writeField( final FieldDescriptorLite descriptor, final Object value, final CodedOutputStream output) throws IOException { WireFormat.FieldType type = descriptor.getLiteType(); int number = descriptor.getNumber(); if (descriptor.isRepeated()) { final List valueList = (List) value; if (descriptor.isPacked()) { output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED); // Compute the total data size so the length can be written. int dataSize = 0; for (final Object element : valueList) { dataSize += computeElementSizeNoTag(type, element); } output.writeUInt32NoTag(dataSize); // Write the data itself, without any tags. for (final Object element : valueList) { writeElementNoTag(output, type, element); } } else { for (final Object element : valueList) { writeElement(output, type, number, element); } } } else { if (value instanceof LazyField) { writeElement(output, type, number, ((LazyField) value).getValue()); } else { writeElement(output, type, number, value); } } } /** * See {@link Message#getSerializedSize()}. It's up to the caller to cache the resulting size if * desired. */ public int getSerializedSize() { int size = 0; for (int i = 0; i < fields.getNumArrayEntries(); i++) { final Map.Entry entry = fields.getArrayEntryAt(i); size += computeFieldSize(entry.getKey(), entry.getValue()); } for (final Map.Entry entry : fields.getOverflowEntries()) { size += computeFieldSize(entry.getKey(), entry.getValue()); } return size; } /** Like {@link #getSerializedSize} but uses MessageSet wire format. */ public int getMessageSetSerializedSize() { int size = 0; for (int i = 0; i < fields.getNumArrayEntries(); i++) { size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); } for (final Map.Entry entry : fields.getOverflowEntries()) { size += getMessageSetSerializedSize(entry); } return size; } private int getMessageSetSerializedSize(final Map.Entry entry) { final T descriptor = entry.getKey(); Object value = entry.getValue(); if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && !descriptor.isRepeated() && !descriptor.isPacked()) { if (value instanceof LazyField) { return CodedOutputStream.computeLazyFieldMessageSetExtensionSize( entry.getKey().getNumber(), (LazyField) value); } else { return CodedOutputStream.computeMessageSetExtensionSize( entry.getKey().getNumber(), (MessageLite) value); } } else { return computeFieldSize(descriptor, value); } } /** * Compute the number of bytes that would be needed to encode a single tag/value pair of arbitrary * type. * * @param type The field's type. * @param number The field's number. * @param value Object representing the field's value. Must be of the exact type which would be * returned by {@link Message#getField(Descriptors.FieldDescriptor)} for this field. */ static int computeElementSize( final WireFormat.FieldType type, final int number, final Object value) { int tagSize = CodedOutputStream.computeTagSize(number); if (type == WireFormat.FieldType.GROUP) { // Only count the end group tag for proto2 messages as for proto1 the end // group tag will be counted as a part of getSerializedSize(). tagSize *= 2; } return tagSize + computeElementSizeNoTag(type, value); } /** * Compute the number of bytes that would be needed to encode a particular value of arbitrary * type, excluding tag. * * @param type The field's type. * @param value Object representing the field's value. Must be of the exact type which would be * returned by {@link Message#getField(Descriptors.FieldDescriptor)} for this field. */ static int computeElementSizeNoTag(final WireFormat.FieldType type, final Object value) { switch (type) { case DOUBLE: return CodedOutputStream.computeDoubleSizeNoTag((Double) value); case FLOAT: return CodedOutputStream.computeFloatSizeNoTag((Float) value); case INT64: return CodedOutputStream.computeInt64SizeNoTag((Long) value); case UINT64: return CodedOutputStream.computeUInt64SizeNoTag((Long) value); case INT32: return CodedOutputStream.computeInt32SizeNoTag((Integer) value); case FIXED64: return CodedOutputStream.computeFixed64SizeNoTag((Long) value); case FIXED32: return CodedOutputStream.computeFixed32SizeNoTag((Integer) value); case BOOL: return CodedOutputStream.computeBoolSizeNoTag((Boolean) value); case GROUP: return CodedOutputStream.computeGroupSizeNoTag((MessageLite) value); case BYTES: if (value instanceof ByteString) { return CodedOutputStream.computeBytesSizeNoTag((ByteString) value); } else { return CodedOutputStream.computeByteArraySizeNoTag((byte[]) value); } case STRING: if (value instanceof ByteString) { return CodedOutputStream.computeBytesSizeNoTag((ByteString) value); } else { return CodedOutputStream.computeStringSizeNoTag((String) value); } case UINT32: return CodedOutputStream.computeUInt32SizeNoTag((Integer) value); case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer) value); case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long) value); case SINT32: return CodedOutputStream.computeSInt32SizeNoTag((Integer) value); case SINT64: return CodedOutputStream.computeSInt64SizeNoTag((Long) value); case MESSAGE: if (value instanceof LazyField) { return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value); } else { return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value); } case ENUM: if (value instanceof Internal.EnumLite) { return CodedOutputStream.computeEnumSizeNoTag(((Internal.EnumLite) value).getNumber()); } else { return CodedOutputStream.computeEnumSizeNoTag((Integer) value); } } throw new RuntimeException("There is no way to get here, but the compiler thinks otherwise."); } /** Compute the number of bytes needed to encode a particular field. */ public static int computeFieldSize(final FieldDescriptorLite descriptor, final Object value) { WireFormat.FieldType type = descriptor.getLiteType(); int number = descriptor.getNumber(); if (descriptor.isRepeated()) { if (descriptor.isPacked()) { int dataSize = 0; for (final Object element : (List) value) { dataSize += computeElementSizeNoTag(type, element); } return dataSize + CodedOutputStream.computeTagSize(number) + CodedOutputStream.computeUInt32SizeNoTag(dataSize); } else { int size = 0; for (final Object element : (List) value) { size += computeElementSize(type, number, element); } return size; } } else { return computeElementSize(type, number, value); } } /** * A FieldSet Builder that accept a {@link MessageLite.Builder} as a field value. This is useful * for implementing methods in {@link MessageLite.Builder}. */ static final class Builder> { private SmallSortedMap fields; private boolean hasLazyField; private boolean isMutable; private boolean hasNestedBuilders; private Builder() { this(SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE)); } private Builder(SmallSortedMap fields) { this.fields = fields; this.isMutable = true; } /** * Creates the FieldSet * * @throws UninitializedMessageException if a message field is missing required fields. */ public FieldSet build() { return buildImpl(false); } /** Creates the FieldSet but does not validate that all required fields are present. */ public FieldSet buildPartial() { return buildImpl(true); } /** * Creates the FieldSet. * * @param partial controls whether to do a build() or buildPartial() when converting submessage * builders to messages. */ private FieldSet buildImpl(boolean partial) { if (fields.isEmpty()) { return FieldSet.emptySet(); } isMutable = false; SmallSortedMap fieldsForBuild = fields; if (hasNestedBuilders) { // Make a copy of the fields map with all Builders replaced by Message. fieldsForBuild = cloneAllFieldsMap(fields, /* copyList */ false); replaceBuilders(fieldsForBuild, partial); } FieldSet fieldSet = new FieldSet<>(fieldsForBuild); fieldSet.hasLazyField = hasLazyField; return fieldSet; } private static > void replaceBuilders( SmallSortedMap fieldMap, boolean partial) { for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { replaceBuilders(fieldMap.getArrayEntryAt(i), partial); } for (Map.Entry entry : fieldMap.getOverflowEntries()) { replaceBuilders(entry, partial); } } private static > void replaceBuilders( Map.Entry entry, boolean partial) { entry.setValue(replaceBuilders(entry.getKey(), entry.getValue(), partial)); } private static > Object replaceBuilders( T descriptor, Object value, boolean partial) { if (value == null) { return value; } if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { if (descriptor.isRepeated()) { if (!(value instanceof List)) { throw new IllegalStateException( "Repeated field should contains a List but actually contains type: " + value.getClass()); } @SuppressWarnings("unchecked") // We just check that value is an instance of List above. List list = (List) value; for (int i = 0; i < list.size(); i++) { Object oldElement = list.get(i); Object newElement = replaceBuilder(oldElement, partial); if (newElement != oldElement) { // If the list contains a Message.Builder, then make a copy of that list and then // modify the Message.Builder into a Message and return the new list. This way, the // existing Message.Builder will still be able to modify the inner fields of the // original FieldSet.Builder. if (list == value) { list = new ArrayList<>(list); } list.set(i, newElement); } } return list; } else { return replaceBuilder(value, partial); } } return value; } private static Object replaceBuilder(Object value, boolean partial) { if (!(value instanceof MessageLite.Builder)) { return value; } MessageLite.Builder builder = (MessageLite.Builder) value; if (partial) { return builder.buildPartial(); } return builder.build(); } /** Returns a new Builder using the fields from {@code fieldSet}. */ public static > Builder fromFieldSet(FieldSet fieldSet) { Builder builder = new Builder(cloneAllFieldsMap(fieldSet.fields, /* copyList */ true)); builder.hasLazyField = fieldSet.hasLazyField; return builder; } // ================================================================= /** Get a simple map containing all the fields. */ public Map getAllFields() { if (hasLazyField) { SmallSortedMap result = cloneAllFieldsMap(fields, /* copyList */ false); if (fields.isImmutable()) { result.makeImmutable(); } else { replaceBuilders(result, true); } return result; } return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); } /** Useful for implementing {@link Message#hasField(Descriptors.FieldDescriptor)}. */ public boolean hasField(final T descriptor) { if (descriptor.isRepeated()) { throw new IllegalArgumentException("hasField() can only be called on non-repeated fields."); } return fields.get(descriptor) != null; } /** * Useful for implementing {@link Message#getField(Descriptors.FieldDescriptor)}. This method * returns {@code null} if the field is not set; in this case it is up to the caller to fetch * the field's default value. */ public Object getField(final T descriptor) { Object value = getFieldAllowBuilders(descriptor); return replaceBuilders(descriptor, value, true); } /** Same as {@link #getField(F)}, but allow a {@link MessageLite.Builder} to be returned. */ Object getFieldAllowBuilders(final T descriptor) { Object o = fields.get(descriptor); if (o instanceof LazyField) { return ((LazyField) o).getValue(); } return o; } private void ensureIsMutable() { if (!isMutable) { fields = cloneAllFieldsMap(fields, /* copyList */ true); isMutable = true; } } /** * Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor, * Object)}. */ @SuppressWarnings({"unchecked", "rawtypes"}) public void setField(final T descriptor, Object value) { ensureIsMutable(); if (descriptor.isRepeated()) { if (!(value instanceof List)) { throw new IllegalArgumentException( "Wrong object type used with protocol message reflection."); } // Wrap the contents in a new list so that the caller cannot change // the list's contents after setting it. final List newList = new ArrayList((List) value); for (final Object element : newList) { verifyType(descriptor, element); hasNestedBuilders = hasNestedBuilders || element instanceof MessageLite.Builder; } value = newList; } else { verifyType(descriptor, value); } if (value instanceof LazyField) { hasLazyField = true; } hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder; fields.put(descriptor, value); } /** Useful for implementing {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ public void clearField(final T descriptor) { ensureIsMutable(); fields.remove(descriptor); if (fields.isEmpty()) { hasLazyField = false; } } /** * Useful for implementing {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */ public int getRepeatedFieldCount(final T descriptor) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedFieldCount() can only be called on repeated fields."); } final Object value = getFieldAllowBuilders(descriptor); if (value == null) { return 0; } else { return ((List) value).size(); } } /** * Useful for implementing {@link Message#getRepeatedField(Descriptors.FieldDescriptor, int)}. */ public Object getRepeatedField(final T descriptor, final int index) { if (hasNestedBuilders) { ensureIsMutable(); } Object value = getRepeatedFieldAllowBuilders(descriptor, index); return replaceBuilder(value, true); } /** * Same as {@link #getRepeatedField(F, int)}, but allow a {@link MessageLite.Builder} to be * returned. */ Object getRepeatedFieldAllowBuilders(final T descriptor, final int index) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedField() can only be called on repeated fields."); } final Object value = getFieldAllowBuilders(descriptor); if (value == null) { throw new IndexOutOfBoundsException(); } else { return ((List) value).get(index); } } /** * Useful for implementing {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor, * int, Object)}. */ @SuppressWarnings("unchecked") public void setRepeatedField(final T descriptor, final int index, final Object value) { ensureIsMutable(); if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedField() can only be called on repeated fields."); } hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder; final Object list = getFieldAllowBuilders(descriptor); if (list == null) { throw new IndexOutOfBoundsException(); } verifyType(descriptor, value); ((List) list).set(index, value); } /** * Useful for implementing {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor, * Object)}. */ @SuppressWarnings("unchecked") public void addRepeatedField(final T descriptor, final Object value) { ensureIsMutable(); if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "addRepeatedField() can only be called on repeated fields."); } hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder; verifyType(descriptor, value); final Object existingValue = getFieldAllowBuilders(descriptor); List list; if (existingValue == null) { list = new ArrayList<>(); fields.put(descriptor, list); } else { list = (List) existingValue; } list.add(value); } /** * Verifies that the given object is of the correct type to be a valid value for the given * field. (For repeated fields, this checks if the object is the right type to be one element of * the field.) * * @throws IllegalArgumentException The value is not of the right type. */ private void verifyType(final T descriptor, final Object value) { if (!FieldSet.isValidType(descriptor.getLiteType(), value)) { // Builder can accept Message.Builder values even though FieldSet will reject. if (descriptor.getLiteType().getJavaType() == WireFormat.JavaType.MESSAGE && value instanceof MessageLite.Builder) { return; } throw new IllegalArgumentException( String.format( "Wrong object type used with protocol message reflection.\n" + "Field number: %d, field java type: %s, value type: %s\n", descriptor.getNumber(), descriptor.getLiteType().getJavaType(), value.getClass().getName())); } } /** * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} itself does not have any * way of knowing about required fields that aren't actually present in the set, it is up to the * caller to check that all required fields are present. */ public boolean isInitialized() { for (int i = 0; i < fields.getNumArrayEntries(); i++) { if (!FieldSet.isInitialized(fields.getArrayEntryAt(i))) { return false; } } for (final Map.Entry entry : fields.getOverflowEntries()) { if (!FieldSet.isInitialized(entry)) { return false; } } return true; } /** * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}. */ public void mergeFrom(final FieldSet other) { ensureIsMutable(); for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { mergeFromField(other.fields.getArrayEntryAt(i)); } for (final Map.Entry entry : other.fields.getOverflowEntries()) { mergeFromField(entry); } } @SuppressWarnings("unchecked") private void mergeFromField(final Map.Entry entry) { final T descriptor = entry.getKey(); Object otherValue = entry.getValue(); if (otherValue instanceof LazyField) { otherValue = ((LazyField) otherValue).getValue(); } if (descriptor.isRepeated()) { List value = (List) getFieldAllowBuilders(descriptor); if (value == null) { value = new ArrayList<>(); fields.put(descriptor, value); } for (Object element : (List) otherValue) { value.add(FieldSet.cloneIfMutable(element)); } } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { Object value = getFieldAllowBuilders(descriptor); if (value == null) { fields.put(descriptor, FieldSet.cloneIfMutable(otherValue)); } else { // Merge the messages. if (value instanceof MessageLite.Builder) { descriptor.internalMergeFrom((MessageLite.Builder) value, (MessageLite) otherValue); } else { value = descriptor .internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue) .build(); fields.put(descriptor, value); } } } else { fields.put(descriptor, cloneIfMutable(otherValue)); } } } }