| // 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 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; |
| |
| /** |
| * 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<FieldDescriptorType extends |
| FieldSet.FieldDescriptorLite<FieldDescriptorType>> { |
| /** |
| * Interface for a FieldDescriptor or lite extension descriptor. This |
| * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}. |
| */ |
| public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>> |
| extends Comparable<T> { |
| 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 final SmallSortedMap<FieldDescriptorType, Object> fields; |
| private boolean isImmutable; |
| private boolean hasLazyField = false; |
| |
| /** Construct a new FieldSet. */ |
| private FieldSet() { |
| this.fields = SmallSortedMap.newFieldMap(16); |
| } |
| |
| /** |
| * Construct an empty FieldSet. This is only used to initialize |
| * DEFAULT_INSTANCE. |
| */ |
| private FieldSet(final boolean dummy) { |
| this.fields = SmallSortedMap.newFieldMap(0); |
| makeImmutable(); |
| } |
| |
| /** Construct a new FieldSet. */ |
| public static <T extends FieldSet.FieldDescriptorLite<T>> |
| FieldSet<T> newFieldSet() { |
| return new FieldSet<T>(); |
| } |
| |
| /** Get an immutable empty FieldSet. */ |
| @SuppressWarnings("unchecked") |
| public static <T extends FieldSet.FieldDescriptorLite<T>> |
| FieldSet<T> emptySet() { |
| return DEFAULT_INSTANCE; |
| } |
| @SuppressWarnings("rawtypes") |
| private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); |
| |
| /** Make this FieldSet immutable from this point forward. */ |
| @SuppressWarnings("unchecked") |
| public void makeImmutable() { |
| if (isImmutable) { |
| return; |
| } |
| 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; |
| } |
| |
| /** |
| * Clones the FieldSet. The returned FieldSet will be mutable even if the |
| * original FieldSet was immutable. |
| * |
| * @return the newly cloned FieldSet |
| */ |
| @Override |
| public FieldSet<FieldDescriptorType> clone() { |
| // We can't just call fields.clone because List objects in the map |
| // should not be shared. |
| FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet(); |
| for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
| Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); |
| FieldDescriptorType descriptor = entry.getKey(); |
| clone.setField(descriptor, entry.getValue()); |
| } |
| for (Map.Entry<FieldDescriptorType, Object> entry : |
| fields.getOverflowEntries()) { |
| FieldDescriptorType descriptor = entry.getKey(); |
| clone.setField(descriptor, 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<FieldDescriptorType, Object> getAllFields() { |
| if (hasLazyField) { |
| SmallSortedMap<FieldDescriptorType, Object> result = |
| SmallSortedMap.newFieldMap(16); |
| for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
| cloneFieldEntry(result, fields.getArrayEntryAt(i)); |
| } |
| for (Map.Entry<FieldDescriptorType, Object> entry : |
| fields.getOverflowEntries()) { |
| cloneFieldEntry(result, entry); |
| } |
| if (fields.isImmutable()) { |
| result.makeImmutable(); |
| } |
| return result; |
| } |
| return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); |
| } |
| |
| private void cloneFieldEntry(Map<FieldDescriptorType, Object> map, |
| Map.Entry<FieldDescriptorType, Object> entry) { |
| FieldDescriptorType key = entry.getKey(); |
| Object value = entry.getValue(); |
| if (value instanceof LazyField) { |
| map.put(key, ((LazyField) value).getValue()); |
| } 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<Map.Entry<FieldDescriptorType, Object>> iterator() { |
| if (hasLazyField) { |
| return new LazyIterator<FieldDescriptorType>( |
| fields.entrySet().iterator()); |
| } |
| return fields.entrySet().iterator(); |
| } |
| |
| /** |
| * Useful for implementing |
| * {@link Message#hasField(Descriptors.FieldDescriptor)}. |
| */ |
| public boolean hasField(final FieldDescriptorType 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 FieldDescriptorType 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 FieldDescriptorType 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.getLiteType(), element); |
| } |
| value = newList; |
| } else { |
| verifyType(descriptor.getLiteType(), value); |
| } |
| |
| if (value instanceof LazyField) { |
| hasLazyField = true; |
| } |
| fields.put(descriptor, value); |
| } |
| |
| /** |
| * Useful for implementing |
| * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. |
| */ |
| public void clearField(final FieldDescriptorType descriptor) { |
| fields.remove(descriptor); |
| if (fields.isEmpty()) { |
| hasLazyField = false; |
| } |
| } |
| |
| /** |
| * Useful for implementing |
| * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. |
| */ |
| public int getRepeatedFieldCount(final FieldDescriptorType 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 FieldDescriptorType 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 FieldDescriptorType 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.getLiteType(), value); |
| ((List<Object>) list).set(index, value); |
| } |
| |
| /** |
| * Useful for implementing |
| * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. |
| */ |
| @SuppressWarnings("unchecked") |
| public void addRepeatedField(final FieldDescriptorType descriptor, |
| final Object value) { |
| if (!descriptor.isRepeated()) { |
| throw new IllegalArgumentException( |
| "addRepeatedField() can only be called on repeated fields."); |
| } |
| |
| verifyType(descriptor.getLiteType(), value); |
| |
| final Object existingValue = getField(descriptor); |
| List<Object> list; |
| if (existingValue == null) { |
| list = new ArrayList<Object>(); |
| fields.put(descriptor, list); |
| } else { |
| list = (List<Object>) 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 static void verifyType(final WireFormat.FieldType type, |
| final Object value) { |
| if (value == null) { |
| throw new NullPointerException(); |
| } |
| |
| boolean isValid = false; |
| switch (type.getJavaType()) { |
| case INT: isValid = value instanceof Integer ; break; |
| case LONG: isValid = value instanceof Long ; break; |
| case FLOAT: isValid = value instanceof Float ; break; |
| case DOUBLE: isValid = value instanceof Double ; break; |
| case BOOLEAN: isValid = value instanceof Boolean ; break; |
| case STRING: isValid = value instanceof String ; break; |
| case BYTE_STRING: |
| isValid = value instanceof ByteString || value instanceof byte[]; |
| break; |
| case ENUM: |
| // TODO(kenton): Caller must do type checking here, I guess. |
| isValid = |
| (value instanceof Integer || value instanceof Internal.EnumLite); |
| break; |
| case MESSAGE: |
| // TODO(kenton): Caller must do type checking here, I guess. |
| isValid = |
| (value instanceof MessageLite) || (value instanceof LazyField); |
| break; |
| } |
| |
| if (!isValid) { |
| // TODO(kenton): When chaining calls to setField(), it can be hard to |
| // tell from the stack trace which exact call failed, since the whole |
| // chain is considered one line of code. It would be nice to print |
| // more information here, e.g. naming the field. We used to do that. |
| // But we can't now that FieldSet doesn't use descriptors. Maybe this |
| // isn't a big deal, though, since it would only really apply when using |
| // reflection and generally people don't chain reflection setters. |
| throw new IllegalArgumentException( |
| "Wrong object type used with protocol message reflection."); |
| } |
| } |
| |
| // ================================================================= |
| // 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<FieldDescriptorType, Object> entry : |
| fields.getOverflowEntries()) { |
| if (!isInitialized(entry)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private boolean isInitialized( |
| final Map.Entry<FieldDescriptorType, Object> entry) { |
| final FieldDescriptorType descriptor = entry.getKey(); |
| if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { |
| if (descriptor.isRepeated()) { |
| for (final MessageLite element: |
| (List<MessageLite>) entry.getValue()) { |
| if (!element.isInitialized()) { |
| return false; |
| } |
| } |
| } else { |
| Object value = entry.getValue(); |
| if (value instanceof MessageLite) { |
| if (!((MessageLite) value).isInitialized()) { |
| return false; |
| } |
| } else if (value instanceof LazyField) { |
| return true; |
| } else { |
| throw new IllegalArgumentException( |
| "Wrong object type used with protocol message reflection."); |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Given a field type, return the wire type. |
| * |
| * @returns 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<FieldDescriptorType> other) { |
| for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { |
| mergeFromField(other.fields.getArrayEntryAt(i)); |
| } |
| for (final Map.Entry<FieldDescriptorType, Object> entry : |
| other.fields.getOverflowEntries()) { |
| mergeFromField(entry); |
| } |
| } |
| |
| private 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<FieldDescriptorType, Object> entry) { |
| final FieldDescriptorType 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)); |
| } |
| } |
| |
| // TODO(kenton): Move static parsing and serialization methods into some |
| // other class. Probably WireFormat. |
| |
| /** |
| * 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 utf8. |
| * @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 { |
| switch (type) { |
| case DOUBLE : return input.readDouble (); |
| case FLOAT : return input.readFloat (); |
| case INT64 : return input.readInt64 (); |
| case UINT64 : return input.readUInt64 (); |
| case INT32 : return input.readInt32 (); |
| case FIXED64 : return input.readFixed64 (); |
| case FIXED32 : return input.readFixed32 (); |
| case BOOL : return input.readBool (); |
| case STRING : if (checkUtf8) { |
| return input.readStringRequireUtf8(); |
| } else { |
| return input.readString(); |
| } |
| case BYTES : return input.readBytes (); |
| case UINT32 : return input.readUInt32 (); |
| case SFIXED32: return input.readSFixed32(); |
| case SFIXED64: return input.readSFixed64(); |
| case SINT32 : return input.readSInt32 (); |
| case SINT64 : return input.readSInt64 (); |
| |
| case GROUP: |
| throw new IllegalArgumentException( |
| "readPrimitiveField() cannot handle nested groups."); |
| case MESSAGE: |
| throw new IllegalArgumentException( |
| "readPrimitiveField() cannot handle embedded messages."); |
| case ENUM: |
| // We don't handle enums because we don't know what to do if the |
| // value is not recognized. |
| throw new IllegalArgumentException( |
| "readPrimitiveField() cannot handle enums."); |
| } |
| |
| throw new RuntimeException( |
| "There is no way to get here, but the compiler thinks otherwise."); |
| } |
| |
| |
| /** See {@link Message#writeTo(CodedOutputStream)}. */ |
| public void writeTo(final CodedOutputStream output) |
| throws IOException { |
| for (int i = 0; i < fields.getNumArrayEntries(); i++) { |
| final Map.Entry<FieldDescriptorType, Object> entry = |
| fields.getArrayEntryAt(i); |
| writeField(entry.getKey(), entry.getValue(), output); |
| } |
| for (final Map.Entry<FieldDescriptorType, Object> 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<FieldDescriptorType, Object> entry : |
| fields.getOverflowEntries()) { |
| writeMessageSetTo(entry, output); |
| } |
| } |
| |
| private void writeMessageSetTo( |
| final Map.Entry<FieldDescriptorType, Object> entry, |
| final CodedOutputStream output) throws IOException { |
| final FieldDescriptorType 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. |
| */ |
| private 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. |
| */ |
| private 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 STRING : output.writeStringNoTag ((String ) value); break; |
| case GROUP : output.writeGroupNoTag ((MessageLite) value); break; |
| case MESSAGE : output.writeMessageNoTag ((MessageLite) 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.writeRawVarint32(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<FieldDescriptorType, Object> entry = |
| fields.getArrayEntryAt(i); |
| size += computeFieldSize(entry.getKey(), entry.getValue()); |
| } |
| for (final Map.Entry<FieldDescriptorType, Object> 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<FieldDescriptorType, Object> entry : |
| fields.getOverflowEntries()) { |
| size += getMessageSetSerializedSize(entry); |
| } |
| return size; |
| } |
| |
| private int getMessageSetSerializedSize( |
| final Map.Entry<FieldDescriptorType, Object> entry) { |
| final FieldDescriptorType 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. |
| */ |
| private 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. |
| */ |
| private static int computeElementSizeNoTag( |
| final WireFormat.FieldType type, final Object value) { |
| switch (type) { |
| // Note: Minor violation of 80-char limit rule here because this would |
| // actually be harder to read if we wrapped the lines. |
| 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 STRING : return CodedOutputStream.computeStringSizeNoTag ((String )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 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.computeRawVarint32Size(dataSize); |
| } else { |
| int size = 0; |
| for (final Object element : (List<?>)value) { |
| size += computeElementSize(type, number, element); |
| } |
| return size; |
| } |
| } else { |
| return computeElementSize(type, number, value); |
| } |
| } |
| } |