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 * This class serves as an utility class to help do serialization/parsing of 41 * map entries. It's used in generated code and also in the full version 42 * MapEntry message. 43 * 44 * Protobuf internal. Users shouldn't use. 45 */ 46 public class MapEntryLite<K, V> { 47 48 static class Metadata<K, V> { 49 public final WireFormat.FieldType keyType; 50 public final K defaultKey; 51 public final WireFormat.FieldType valueType; 52 public final V defaultValue; 53 Metadata( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)54 public Metadata( 55 WireFormat.FieldType keyType, K defaultKey, 56 WireFormat.FieldType valueType, V defaultValue) { 57 this.keyType = keyType; 58 this.defaultKey = defaultKey; 59 this.valueType = valueType; 60 this.defaultValue = defaultValue; 61 } 62 } 63 64 private static final int KEY_FIELD_NUMBER = 1; 65 private static final int VALUE_FIELD_NUMBER = 2; 66 67 private final Metadata<K, V> metadata; 68 private final K key; 69 private final V value; 70 71 /** Creates a default MapEntryLite message instance. */ MapEntryLite( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)72 private MapEntryLite( 73 WireFormat.FieldType keyType, K defaultKey, 74 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 * This method is used by generated code to create the default instance for 99 * a map entry message. The created default instance should be used to create 100 * new map entry messages of the same type. For each map entry message, only 101 * one default instance should be created. 102 */ newDefaultInstance( WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue)103 public static <K, V> MapEntryLite<K, V> newDefaultInstance( 104 WireFormat.FieldType keyType, K defaultKey, 105 WireFormat.FieldType valueType, V defaultValue) { 106 return new MapEntryLite<K, V>( 107 keyType, defaultKey, valueType, defaultValue); 108 } 109 writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)110 static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value) 111 throws IOException { 112 FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key); 113 FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value); 114 } 115 computeSerializedSize(Metadata<K, V> metadata, K key, V value)116 static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) { 117 return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key) 118 + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value); 119 } 120 121 @SuppressWarnings("unchecked") parseField( CodedInputStream input, ExtensionRegistryLite extensionRegistry, WireFormat.FieldType type, T value)122 static <T> T parseField( 123 CodedInputStream input, ExtensionRegistryLite extensionRegistry, 124 WireFormat.FieldType type, T value) 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} 141 * to the output stream. This helper method avoids allocation of a {@link MapEntryLite} 142 * built with a 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 153 * by a {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite} 154 * built with 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 164 * so 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 196 * {@link 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