232 lines
8.6 KiB
Java
232 lines
8.6 KiB
Java
// 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.AbstractMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Implements the lite version of map entry messages.
|
|
*
|
|
* <p>This class serves as an utility class to help do serialization/parsing of map entries. It's
|
|
* used in generated code and also in the full version MapEntry message.
|
|
*
|
|
* <p>Protobuf internal. Users shouldn't use.
|
|
*/
|
|
public class MapEntryLite<K, V> {
|
|
|
|
static class Metadata<K, V> {
|
|
public final WireFormat.FieldType keyType;
|
|
public final K defaultKey;
|
|
public final WireFormat.FieldType valueType;
|
|
public final V defaultValue;
|
|
|
|
public Metadata(
|
|
WireFormat.FieldType keyType,
|
|
K defaultKey,
|
|
WireFormat.FieldType valueType,
|
|
V defaultValue) {
|
|
this.keyType = keyType;
|
|
this.defaultKey = defaultKey;
|
|
this.valueType = valueType;
|
|
this.defaultValue = defaultValue;
|
|
}
|
|
}
|
|
|
|
private static final int KEY_FIELD_NUMBER = 1;
|
|
private static final int VALUE_FIELD_NUMBER = 2;
|
|
|
|
private final Metadata<K, V> metadata;
|
|
private final K key;
|
|
private final V value;
|
|
|
|
/** Creates a default MapEntryLite message instance. */
|
|
private MapEntryLite(
|
|
WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) {
|
|
this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue);
|
|
this.key = defaultKey;
|
|
this.value = defaultValue;
|
|
}
|
|
|
|
/** Creates a new MapEntryLite message. */
|
|
private MapEntryLite(Metadata<K, V> metadata, K key, V value) {
|
|
this.metadata = metadata;
|
|
this.key = key;
|
|
this.value = value;
|
|
}
|
|
|
|
public K getKey() {
|
|
return key;
|
|
}
|
|
|
|
public V getValue() {
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Creates a default MapEntryLite message instance.
|
|
*
|
|
* <p>This method is used by generated code to create the default instance for a map entry
|
|
* message. The created default instance should be used to create new map entry messages of the
|
|
* same type. For each map entry message, only one default instance should be created.
|
|
*/
|
|
public static <K, V> MapEntryLite<K, V> newDefaultInstance(
|
|
WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) {
|
|
return new MapEntryLite<K, V>(keyType, defaultKey, valueType, defaultValue);
|
|
}
|
|
|
|
static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)
|
|
throws IOException {
|
|
FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key);
|
|
FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value);
|
|
}
|
|
|
|
static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) {
|
|
return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key)
|
|
+ FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
static <T> T parseField(
|
|
CodedInputStream input,
|
|
ExtensionRegistryLite extensionRegistry,
|
|
WireFormat.FieldType type,
|
|
T value)
|
|
throws IOException {
|
|
switch (type) {
|
|
case MESSAGE:
|
|
MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder();
|
|
input.readMessage(subBuilder, extensionRegistry);
|
|
return (T) subBuilder.buildPartial();
|
|
case ENUM:
|
|
return (T) (java.lang.Integer) input.readEnum();
|
|
case GROUP:
|
|
throw new RuntimeException("Groups are not allowed in maps.");
|
|
default:
|
|
return (T) FieldSet.readPrimitiveField(input, type, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite} to
|
|
* the output stream. This helper method avoids allocation of a {@link MapEntryLite} built with a
|
|
* key and value and is called from generated code directly.
|
|
*/
|
|
public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)
|
|
throws IOException {
|
|
output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
|
output.writeUInt32NoTag(computeSerializedSize(metadata, key, value));
|
|
writeTo(output, metadata, key, value);
|
|
}
|
|
|
|
/**
|
|
* Computes the message size for the provided key and value as though they were wrapped by a
|
|
* {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite} built with
|
|
* a key and value and is called from generated code directly.
|
|
*/
|
|
public int computeMessageSize(int fieldNumber, K key, V value) {
|
|
return CodedOutputStream.computeTagSize(fieldNumber)
|
|
+ CodedOutputStream.computeLengthDelimitedFieldSize(
|
|
computeSerializedSize(metadata, key, value));
|
|
}
|
|
|
|
/**
|
|
* Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation so
|
|
* using {@link #parseInto} is preferred if possible.
|
|
*/
|
|
public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)
|
|
throws IOException {
|
|
return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry);
|
|
}
|
|
|
|
static <K, V> Map.Entry<K, V> parseEntry(
|
|
CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)
|
|
throws IOException {
|
|
K key = metadata.defaultKey;
|
|
V value = metadata.defaultValue;
|
|
while (true) {
|
|
int tag = input.readTag();
|
|
if (tag == 0) {
|
|
break;
|
|
}
|
|
if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
|
|
key = parseField(input, extensionRegistry, metadata.keyType, key);
|
|
} else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
|
|
value = parseField(input, extensionRegistry, metadata.valueType, value);
|
|
} else {
|
|
if (!input.skipField(tag)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
|
|
}
|
|
|
|
/**
|
|
* Parses an entry off of the input into the map. This helper avoids allocation of a {@link
|
|
* MapEntryLite} by parsing directly into the provided {@link MapFieldLite}.
|
|
*/
|
|
public void parseInto(
|
|
MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
|
throws IOException {
|
|
int length = input.readRawVarint32();
|
|
final int oldLimit = input.pushLimit(length);
|
|
K key = metadata.defaultKey;
|
|
V value = metadata.defaultValue;
|
|
|
|
while (true) {
|
|
int tag = input.readTag();
|
|
if (tag == 0) {
|
|
break;
|
|
}
|
|
if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
|
|
key = parseField(input, extensionRegistry, metadata.keyType, key);
|
|
} else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
|
|
value = parseField(input, extensionRegistry, metadata.valueType, value);
|
|
} else {
|
|
if (!input.skipField(tag)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
input.checkLastTagWas(0);
|
|
input.popLimit(oldLimit);
|
|
map.put(key, value);
|
|
}
|
|
|
|
/** For experimental runtime internal use only. */
|
|
Metadata<K, V> getMetadata() {
|
|
return metadata;
|
|
}
|
|
}
|