1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import java.io.IOException; 34 import java.util.AbstractMap; 35 import java.util.Map; 36 37 /** 38 * Implements the lite version of map entry messages. 39 * 40 * <p>This class serves as an utility class to help do serialization/parsing of map entries. It's 41 * used in generated code and also in the full version MapEntry message. 42 * 43 * <p>Protobuf internal. Users shouldn't use. 44 */ 45 public class MapEntryLite<K, V> { 46 47 static class Metadata<K, V> { 48 public final WireFormat.FieldType keyType; 49 public final K defaultKey; 50 public final WireFormat.FieldType valueType; 51 public final V defaultValue; 52 Metadata( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)53 public Metadata( 54 WireFormat.FieldType keyType, 55 K defaultKey, 56 WireFormat.FieldType valueType, 57 V defaultValue) { 58 this.keyType = keyType; 59 this.defaultKey = defaultKey; 60 this.valueType = valueType; 61 this.defaultValue = defaultValue; 62 } 63 } 64 65 private static final int KEY_FIELD_NUMBER = 1; 66 private static final int VALUE_FIELD_NUMBER = 2; 67 68 private final Metadata<K, V> metadata; 69 private final K key; 70 private final V value; 71 72 /** Creates a default MapEntryLite message instance. */ MapEntryLite( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)73 private MapEntryLite( 74 WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) { 75 this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue); 76 this.key = defaultKey; 77 this.value = defaultValue; 78 } 79 80 /** Creates a new MapEntryLite message. */ MapEntryLite(Metadata<K, V> metadata, K key, V value)81 private MapEntryLite(Metadata<K, V> metadata, K key, V value) { 82 this.metadata = metadata; 83 this.key = key; 84 this.value = value; 85 } 86 getKey()87 public K getKey() { 88 return key; 89 } 90 getValue()91 public V getValue() { 92 return value; 93 } 94 95 /** 96 * Creates a default MapEntryLite message instance. 97 * 98 * <p>This method is used by generated code to create the default instance for a map entry 99 * message. The created default instance should be used to create new map entry messages of the 100 * same type. For each map entry message, only one default instance should be created. 101 */ newDefaultInstance( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)102 public static <K, V> MapEntryLite<K, V> newDefaultInstance( 103 WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) { 104 return new MapEntryLite<K, V>(keyType, defaultKey, valueType, defaultValue); 105 } 106 writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)107 static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value) 108 throws IOException { 109 FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key); 110 FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value); 111 } 112 computeSerializedSize(Metadata<K, V> metadata, K key, V value)113 static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) { 114 return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key) 115 + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value); 116 } 117 118 @SuppressWarnings("unchecked") parseField( CodedInputStream input, ExtensionRegistryLite extensionRegistry, WireFormat.FieldType type, T value)119 static <T> T parseField( 120 CodedInputStream input, 121 ExtensionRegistryLite extensionRegistry, 122 WireFormat.FieldType type, 123 T value) 124 throws IOException { 125 switch (type) { 126 case MESSAGE: 127 MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder(); 128 input.readMessage(subBuilder, extensionRegistry); 129 return (T) subBuilder.buildPartial(); 130 case ENUM: 131 return (T) (java.lang.Integer) input.readEnum(); 132 case GROUP: 133 throw new RuntimeException("Groups are not allowed in maps."); 134 default: 135 return (T) FieldSet.readPrimitiveField(input, type, true); 136 } 137 } 138 139 /** 140 * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite} to 141 * the output stream. This helper method avoids allocation of a {@link MapEntryLite} built with a 142 * key and value and is called from generated code directly. 143 */ serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)144 public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value) 145 throws IOException { 146 output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); 147 output.writeUInt32NoTag(computeSerializedSize(metadata, key, value)); 148 writeTo(output, metadata, key, value); 149 } 150 151 /** 152 * Computes the message size for the provided key and value as though they were wrapped by a 153 * {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite} built with 154 * a key and value and is called from generated code directly. 155 */ computeMessageSize(int fieldNumber, K key, V value)156 public int computeMessageSize(int fieldNumber, K key, V value) { 157 return CodedOutputStream.computeTagSize(fieldNumber) 158 + CodedOutputStream.computeLengthDelimitedFieldSize( 159 computeSerializedSize(metadata, key, value)); 160 } 161 162 /** 163 * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation so 164 * using {@link #parseInto} is preferred if possible. 165 */ parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)166 public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry) 167 throws IOException { 168 return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry); 169 } 170 parseEntry( CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)171 static <K, V> Map.Entry<K, V> parseEntry( 172 CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry) 173 throws IOException { 174 K key = metadata.defaultKey; 175 V value = metadata.defaultValue; 176 while (true) { 177 int tag = input.readTag(); 178 if (tag == 0) { 179 break; 180 } 181 if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { 182 key = parseField(input, extensionRegistry, metadata.keyType, key); 183 } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { 184 value = parseField(input, extensionRegistry, metadata.valueType, value); 185 } else { 186 if (!input.skipField(tag)) { 187 break; 188 } 189 } 190 } 191 return new AbstractMap.SimpleImmutableEntry<K, V>(key, value); 192 } 193 194 /** 195 * Parses an entry off of the input into the map. This helper avoids allocaton of a {@link 196 * MapEntryLite} by parsing directly into the provided {@link MapFieldLite}. 197 */ parseInto( MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)198 public void parseInto( 199 MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry) 200 throws IOException { 201 int length = input.readRawVarint32(); 202 final int oldLimit = input.pushLimit(length); 203 K key = metadata.defaultKey; 204 V value = metadata.defaultValue; 205 206 while (true) { 207 int tag = input.readTag(); 208 if (tag == 0) { 209 break; 210 } 211 if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { 212 key = parseField(input, extensionRegistry, metadata.keyType, key); 213 } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { 214 value = parseField(input, extensionRegistry, metadata.valueType, value); 215 } else { 216 if (!input.skipField(tag)) { 217 break; 218 } 219 } 220 } 221 222 input.checkLastTagWas(0); 223 input.popLimit(oldLimit); 224 map.put(key, value); 225 } 226 227 /** For experimental runtime internal use only. */ getMetadata()228 Metadata<K, V> getMetadata() { 229 return metadata; 230 } 231 } 232