• 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  * <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