• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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