// 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 java.io.IOException;
import java.util.Iterator;
import java.util.Map.Entry;

Schema used for proto2 messages using message_set_wireformat.
/** Schema used for proto2 messages using message_set_wireformat. */
final class MessageSetSchema<T> implements Schema<T> { private final MessageLite defaultInstance; private final UnknownFieldSchema<?, ?> unknownFieldSchema; private final boolean hasExtensions; private final ExtensionSchema<?> extensionSchema; private MessageSetSchema( UnknownFieldSchema<?, ?> unknownFieldSchema, ExtensionSchema<?> extensionSchema, MessageLite defaultInstance) { this.unknownFieldSchema = unknownFieldSchema; this.hasExtensions = extensionSchema.hasExtensions(defaultInstance); this.extensionSchema = extensionSchema; this.defaultInstance = defaultInstance; } static <T> MessageSetSchema<T> newSchema( UnknownFieldSchema<?, ?> unknownFieldSchema, ExtensionSchema<?> extensionSchema, MessageLite defaultInstance) { return new MessageSetSchema<T>(unknownFieldSchema, extensionSchema, defaultInstance); } @SuppressWarnings("unchecked") @Override public T newInstance() { return (T) defaultInstance.newBuilderForType().buildPartial(); } @Override public boolean equals(T message, T other) { Object messageUnknown = unknownFieldSchema.getFromMessage(message); Object otherUnknown = unknownFieldSchema.getFromMessage(other); if (!messageUnknown.equals(otherUnknown)) { return false; } if (hasExtensions) { FieldSet<?> messageExtensions = extensionSchema.getExtensions(message); FieldSet<?> otherExtensions = extensionSchema.getExtensions(other); return messageExtensions.equals(otherExtensions); } return true; } @Override public int hashCode(T message) { int hashCode = unknownFieldSchema.getFromMessage(message).hashCode(); if (hasExtensions) { FieldSet<?> extensions = extensionSchema.getExtensions(message); hashCode = (hashCode * 53) + extensions.hashCode(); } return hashCode; } @Override public void mergeFrom(T message, T other) { SchemaUtil.mergeUnknownFields(unknownFieldSchema, message, other); if (hasExtensions) { SchemaUtil.mergeExtensions(extensionSchema, message, other); } } @SuppressWarnings("unchecked") @Override public void writeTo(T message, Writer writer) throws IOException { FieldSet<?> extensions = extensionSchema.getExtensions(message); Iterator<?> iterator = extensions.iterator(); while (iterator.hasNext()) { Entry<?, ?> extension = (Entry<?, ?>) iterator.next(); FieldSet.FieldDescriptorLite<?> fd = (FieldSet.FieldDescriptorLite<?>) extension.getKey(); if (fd.getLiteJavaType() != WireFormat.JavaType.MESSAGE || fd.isRepeated() || fd.isPacked()) { throw new IllegalStateException("Found invalid MessageSet item."); } if (extension instanceof LazyField.LazyEntry) { writer.writeMessageSetItem( fd.getNumber(), ((LazyField.LazyEntry) extension).getField().toByteString()); } else { writer.writeMessageSetItem(fd.getNumber(), extension.getValue()); } } writeUnknownFieldsHelper(unknownFieldSchema, message, writer); }
A helper method for wildcard capture of unknownFieldSchema. See: https://docs.oracle.com/javase/tutorial/java/generics/capture.html
/** * A helper method for wildcard capture of {@code unknownFieldSchema}. See: * https://docs.oracle.com/javase/tutorial/java/generics/capture.html */
private <UT, UB> void writeUnknownFieldsHelper( UnknownFieldSchema<UT, UB> unknownFieldSchema, T message, Writer writer) throws IOException { unknownFieldSchema.writeAsMessageSetTo(unknownFieldSchema.getFromMessage(message), writer); } @SuppressWarnings("ReferenceEquality") @Override public void mergeFrom( T message, byte[] data, int position, int limit, ArrayDecoders.Registers registers) throws IOException { UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { unknownFields = UnknownFieldSetLite.newInstance(); ((GeneratedMessageLite) message).unknownFields = unknownFields; } final FieldSet<GeneratedMessageLite.ExtensionDescriptor> extensions = ((GeneratedMessageLite.ExtendableMessage<?, ?>) message).ensureExtensionsAreMutable(); GeneratedMessageLite.GeneratedExtension<?, ?> extension = null; while (position < limit) { position = ArrayDecoders.decodeVarint32(data, position, registers); final int startTag = registers.int1; if (startTag != WireFormat.MESSAGE_SET_ITEM_TAG) { if (WireFormat.getTagWireType(startTag) == WireFormat.WIRETYPE_LENGTH_DELIMITED) { extension = (GeneratedMessageLite.GeneratedExtension<?, ?>) extensionSchema.findExtensionByNumber( registers.extensionRegistry, defaultInstance, WireFormat.getTagFieldNumber(startTag)); if (extension != null) { position = ArrayDecoders.decodeMessageField( Protobuf.getInstance().schemaFor( extension.getMessageDefaultInstance().getClass()), data, position, limit, registers); extensions.setField(extension.descriptor, registers.object1); } else { position = ArrayDecoders.decodeUnknownField( startTag, data, position, limit, unknownFields, registers); } } else { position = ArrayDecoders.skipField(startTag, data, position, limit, registers); } continue; } int typeId = 0; ByteString rawBytes = null; while (position < limit) { position = ArrayDecoders.decodeVarint32(data, position, registers); final int tag = registers.int1; final int number = WireFormat.getTagFieldNumber(tag); final int wireType = WireFormat.getTagWireType(tag); switch (number) { case WireFormat.MESSAGE_SET_TYPE_ID: if (wireType == WireFormat.WIRETYPE_VARINT) { position = ArrayDecoders.decodeVarint32(data, position, registers); typeId = registers.int1; extension = (GeneratedMessageLite.GeneratedExtension<?, ?>) extensionSchema .findExtensionByNumber(registers.extensionRegistry, defaultInstance, typeId); continue; } break; case WireFormat.MESSAGE_SET_MESSAGE: if (extension != null) { position = ArrayDecoders.decodeMessageField( Protobuf.getInstance().schemaFor( extension.getMessageDefaultInstance().getClass()), data, position, limit, registers); extensions.setField(extension.descriptor, registers.object1); continue; } else { if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) { position = ArrayDecoders.decodeBytes(data, position, registers); rawBytes = (ByteString) registers.object1; continue; } break; } default: break; } if (tag == WireFormat.MESSAGE_SET_ITEM_END_TAG) { break; } position = ArrayDecoders.skipField(tag, data, position, limit, registers); } if (rawBytes != null) { unknownFields.storeField( WireFormat.makeTag(typeId, WireFormat.WIRETYPE_LENGTH_DELIMITED), rawBytes); } } if (position != limit) { throw InvalidProtocolBufferException.parseFailure(); } } @Override public void mergeFrom(T message, Reader reader, ExtensionRegistryLite extensionRegistry) throws IOException { mergeFromHelper(unknownFieldSchema, extensionSchema, message, reader, extensionRegistry); }
A helper method for wildcard capture of unknownFieldSchema. See: https://docs.oracle.com/javase/tutorial/java/generics/capture.html
/** * A helper method for wildcard capture of {@code unknownFieldSchema}. See: * https://docs.oracle.com/javase/tutorial/java/generics/capture.html */
@SuppressWarnings("unchecked") private <UT, UB, ET extends FieldSet.FieldDescriptorLite<ET>> void mergeFromHelper( UnknownFieldSchema<UT, UB> unknownFieldSchema, ExtensionSchema<ET> extensionSchema, T message, Reader reader, ExtensionRegistryLite extensionRegistry) throws IOException { UB unknownFields = unknownFieldSchema.getBuilderFromMessage(message); FieldSet<ET> extensions = extensionSchema.getMutableExtensions(message); try { while (true) { final int number = reader.getFieldNumber(); if (number == Reader.READ_DONE) { return; } if (parseMessageSetItemOrUnknownField( reader, extensionRegistry, extensionSchema, extensions, unknownFieldSchema, unknownFields)) { continue; } // Done reading. return; } } finally { unknownFieldSchema.setBuilderToMessage(message, unknownFields); } } @Override public void makeImmutable(T message) { unknownFieldSchema.makeImmutable(message); extensionSchema.makeImmutable(message); } private <UT, UB, ET extends FieldSet.FieldDescriptorLite<ET>> boolean parseMessageSetItemOrUnknownField( Reader reader, ExtensionRegistryLite extensionRegistry, ExtensionSchema<ET> extensionSchema, FieldSet<ET> extensions, UnknownFieldSchema<UT, UB> unknownFieldSchema, UB unknownFields) throws IOException { int startTag = reader.getTag(); if (startTag != WireFormat.MESSAGE_SET_ITEM_TAG) { if (WireFormat.getTagWireType(startTag) == WireFormat.WIRETYPE_LENGTH_DELIMITED) { Object extension = extensionSchema.findExtensionByNumber( extensionRegistry, defaultInstance, WireFormat.getTagFieldNumber(startTag)); if (extension != null) { extensionSchema.parseLengthPrefixedMessageSetItem( reader, extension, extensionRegistry, extensions); return true; } else { return unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader); } } else { return reader.skipField(); } } // The wire format for MessageSet is: // message MessageSet { // repeated group Item = 1 { // required int32 typeId = 2; // required bytes message = 3; // } // } // "typeId" is the extension's field number. The extension can only be // a message type, where "message" contains the encoded bytes of that // message. // // In practice, we will probably never see a MessageSet item in which // the message appears before the type ID, or where either field does not // appear exactly once. However, in theory such cases are valid, so we // should be prepared to accept them. int typeId = 0; ByteString rawBytes = null; // If we encounter "message" before "typeId" Object extension = null; // Read bytes from input, if we get it's type first then parse it eagerly, // otherwise we store the raw bytes in a local variable. loop: while (true) { final int number = reader.getFieldNumber(); if (number == Reader.READ_DONE) { break; } final int tag = reader.getTag(); if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { typeId = reader.readUInt32(); extension = extensionSchema.findExtensionByNumber(extensionRegistry, defaultInstance, typeId); continue; } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { if (extension != null) { extensionSchema.parseLengthPrefixedMessageSetItem( reader, extension, extensionRegistry, extensions); continue; } // We haven't seen a type ID yet or we want parse message lazily. rawBytes = reader.readBytes(); continue; } else { if (!reader.skipField()) { break loop; // End of group } } } if (reader.getTag() != WireFormat.MESSAGE_SET_ITEM_END_TAG) { throw InvalidProtocolBufferException.invalidEndTag(); } // If there are any rawBytes left, it means the message content appears before the type ID. if (rawBytes != null) { if (extension != null) { // We known the type // TODO(xiaofeng): Instead of reading into a temporary ByteString, maybe there is a way // to read directly from Reader to the submessage? extensionSchema.parseMessageSetItem(rawBytes, extension, extensionRegistry, extensions); } else { unknownFieldSchema.addLengthDelimited(unknownFields, typeId, rawBytes); } } return true; } @Override public final boolean isInitialized(T message) { FieldSet<?> extensions = extensionSchema.getExtensions(message); return extensions.isInitialized(); } @Override public int getSerializedSize(T message) { int size = 0; size += getUnknownFieldsSerializedSize(unknownFieldSchema, message); if (hasExtensions) { size += extensionSchema.getExtensions(message).getMessageSetSerializedSize(); } return size; } private <UT, UB> int getUnknownFieldsSerializedSize( UnknownFieldSchema<UT, UB> schema, T message) { UT unknowns = schema.getFromMessage(message); return schema.getSerializedSizeAsMessageSet(unknowns); } }