1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import java.io.IOException; 11 import java.util.AbstractMap; 12 import java.util.Map; 13 14 /** 15 * Implements the lite version of map entry messages. 16 * 17 * <p>This class serves as an utility class to help do serialization/parsing of map entries. It's 18 * used in generated code and also in the full version MapEntry message. 19 * 20 * <p>Protobuf internal. Users shouldn't use. 21 */ 22 public class MapEntryLite<K, V> { 23 24 static class Metadata<K, V> { 25 public final WireFormat.FieldType keyType; 26 public final K defaultKey; 27 public final WireFormat.FieldType valueType; 28 public final V defaultValue; 29 Metadata( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)30 public Metadata( 31 WireFormat.FieldType keyType, 32 K defaultKey, 33 WireFormat.FieldType valueType, 34 V defaultValue) { 35 this.keyType = keyType; 36 this.defaultKey = defaultKey; 37 this.valueType = valueType; 38 this.defaultValue = defaultValue; 39 } 40 } 41 42 private static final int KEY_FIELD_NUMBER = 1; 43 private static final int VALUE_FIELD_NUMBER = 2; 44 45 private final Metadata<K, V> metadata; 46 private final K key; 47 private final V value; 48 49 /** Creates a default MapEntryLite message instance. */ MapEntryLite( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)50 private MapEntryLite( 51 WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) { 52 this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue); 53 this.key = defaultKey; 54 this.value = defaultValue; 55 } 56 57 /** Creates a new MapEntryLite message. */ MapEntryLite(Metadata<K, V> metadata, K key, V value)58 private MapEntryLite(Metadata<K, V> metadata, K key, V value) { 59 this.metadata = metadata; 60 this.key = key; 61 this.value = value; 62 } 63 getKey()64 public K getKey() { 65 return key; 66 } 67 getValue()68 public V getValue() { 69 return value; 70 } 71 72 /** 73 * Creates a default MapEntryLite message instance. 74 * 75 * <p>This method is used by generated code to create the default instance for a map entry 76 * message. The created default instance should be used to create new map entry messages of the 77 * same type. For each map entry message, only one default instance should be created. 78 */ newDefaultInstance( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)79 public static <K, V> MapEntryLite<K, V> newDefaultInstance( 80 WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) { 81 return new MapEntryLite<K, V>(keyType, defaultKey, valueType, defaultValue); 82 } 83 writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)84 static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value) 85 throws IOException { 86 FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key); 87 FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value); 88 } 89 computeSerializedSize(Metadata<K, V> metadata, K key, V value)90 static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) { 91 return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key) 92 + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value); 93 } 94 95 @SuppressWarnings("unchecked") parseField( CodedInputStream input, ExtensionRegistryLite extensionRegistry, WireFormat.FieldType type, T value)96 static <T> T parseField( 97 CodedInputStream input, 98 ExtensionRegistryLite extensionRegistry, 99 WireFormat.FieldType type, 100 T value) 101 throws IOException { 102 switch (type) { 103 case MESSAGE: 104 MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder(); 105 input.readMessage(subBuilder, extensionRegistry); 106 return (T) subBuilder.buildPartial(); 107 case ENUM: 108 return (T) (java.lang.Integer) input.readEnum(); 109 case GROUP: 110 throw new RuntimeException("Groups are not allowed in maps."); 111 default: 112 return (T) FieldSet.readPrimitiveField(input, type, true); 113 } 114 } 115 116 /** 117 * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite} to 118 * the output stream. This helper method avoids allocation of a {@link MapEntryLite} built with a 119 * key and value and is called from generated code directly. 120 */ serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)121 public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value) 122 throws IOException { 123 output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); 124 output.writeUInt32NoTag(computeSerializedSize(metadata, key, value)); 125 writeTo(output, metadata, key, value); 126 } 127 128 /** 129 * Computes the message size for the provided key and value as though they were wrapped by a 130 * {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite} built with 131 * a key and value and is called from generated code directly. 132 */ computeMessageSize(int fieldNumber, K key, V value)133 public int computeMessageSize(int fieldNumber, K key, V value) { 134 return CodedOutputStream.computeTagSize(fieldNumber) 135 + CodedOutputStream.computeLengthDelimitedFieldSize( 136 computeSerializedSize(metadata, key, value)); 137 } 138 139 /** 140 * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation so 141 * using {@link #parseInto} is preferred if possible. 142 */ parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)143 public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry) 144 throws IOException { 145 return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry); 146 } 147 parseEntry( CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)148 static <K, V> Map.Entry<K, V> parseEntry( 149 CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry) 150 throws IOException { 151 K key = metadata.defaultKey; 152 V value = metadata.defaultValue; 153 while (true) { 154 int tag = input.readTag(); 155 if (tag == 0) { 156 break; 157 } 158 if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { 159 key = parseField(input, extensionRegistry, metadata.keyType, key); 160 } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { 161 value = parseField(input, extensionRegistry, metadata.valueType, value); 162 } else { 163 if (!input.skipField(tag)) { 164 break; 165 } 166 } 167 } 168 return new AbstractMap.SimpleImmutableEntry<K, V>(key, value); 169 } 170 171 /** 172 * Parses an entry off of the input into the map. This helper avoids allocation of a {@link 173 * MapEntryLite} by parsing directly into the provided {@link MapFieldLite}. 174 */ parseInto( MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)175 public void parseInto( 176 MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry) 177 throws IOException { 178 int length = input.readRawVarint32(); 179 final int oldLimit = input.pushLimit(length); 180 K key = metadata.defaultKey; 181 V value = metadata.defaultValue; 182 183 while (true) { 184 int tag = input.readTag(); 185 if (tag == 0) { 186 break; 187 } 188 if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { 189 key = parseField(input, extensionRegistry, metadata.keyType, key); 190 } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { 191 value = parseField(input, extensionRegistry, metadata.valueType, value); 192 } else { 193 if (!input.skipField(tag)) { 194 break; 195 } 196 } 197 } 198 199 input.checkLastTagWas(0); 200 input.popLimit(oldLimit); 201 map.put(key, value); 202 } 203 204 /** For experimental runtime internal use only. */ getMetadata()205 Metadata<K, V> getMetadata() { 206 return metadata; 207 } 208 } 209