• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
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.util.ArrayList;
34 import java.util.Collections;
35 import java.util.Iterator;
36 import java.util.TreeMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.io.IOException;
40 
41 /**
42  * A class which represents an arbitrary set of fields of some message type.
43  * This is used to implement {@link DynamicMessage}, and also to represent
44  * extensions in {@link GeneratedMessage}.  This class is package-private,
45  * since outside users should probably be using {@link DynamicMessage}.
46  *
47  * @author kenton@google.com Kenton Varda
48  */
49 final class FieldSet<FieldDescriptorType extends
50       FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
51   /**
52    * Interface for a FieldDescriptor or lite extension descriptor.  This
53    * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
54    */
55   public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
56       extends Comparable<T> {
getNumber()57     int getNumber();
getLiteType()58     WireFormat.FieldType getLiteType();
getLiteJavaType()59     WireFormat.JavaType getLiteJavaType();
isRepeated()60     boolean isRepeated();
isPacked()61     boolean isPacked();
getEnumType()62     Internal.EnumLiteMap<?> getEnumType();
63 
64     // If getLiteJavaType() == MESSAGE, this merges a message object of the
65     // type into a builder of the type.  Returns {@code to}.
internalMergeFrom( MessageLite.Builder to, MessageLite from)66     MessageLite.Builder internalMergeFrom(
67         MessageLite.Builder to, MessageLite from);
68   }
69 
70   private Map<FieldDescriptorType, Object> fields;
71 
72   /** Construct a new FieldSet. */
FieldSet()73   private FieldSet() {
74     // Use a TreeMap because fields need to be in canonical order when
75     // serializing.
76     // TODO(kenton):  Maybe use some sort of sparse array instead?  It would
77     //   even make sense to store the first 16 or so tags in a flat array
78     //   to make DynamicMessage faster.
79     fields = new TreeMap<FieldDescriptorType, Object>();
80   }
81 
82   /**
83    * Construct an empty FieldSet.  This is only used to initialize
84    * DEFAULT_INSTANCE.
85    */
FieldSet(final boolean dummy)86   private FieldSet(final boolean dummy) {
87     this.fields = Collections.emptyMap();
88   }
89 
90   /** Construct a new FieldSet. */
91   public static <T extends FieldSet.FieldDescriptorLite<T>>
newFieldSet()92       FieldSet<T> newFieldSet() {
93     return new FieldSet<T>();
94   }
95 
96   /** Get an immutable empty FieldSet. */
97   @SuppressWarnings("unchecked")
98   public static <T extends FieldSet.FieldDescriptorLite<T>>
emptySet()99       FieldSet<T> emptySet() {
100     return DEFAULT_INSTANCE;
101   }
102   @SuppressWarnings("unchecked")
103   private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
104 
105   /** Make this FieldSet immutable from this point forward. */
106   @SuppressWarnings("unchecked")
makeImmutable()107   public void makeImmutable() {
108     for (final Map.Entry<FieldDescriptorType, Object> entry:
109          fields.entrySet()) {
110       if (entry.getKey().isRepeated()) {
111         final List value = (List)entry.getValue();
112         fields.put(entry.getKey(), Collections.unmodifiableList(value));
113       }
114     }
115     fields = Collections.unmodifiableMap(fields);
116   }
117 
118   // =================================================================
119 
120   /** See {@link Message.Builder#clear()}. */
clear()121   public void clear() {
122     fields.clear();
123   }
124 
125   /**
126    * Get a simple map containing all the fields.
127    */
getAllFields()128   public Map<FieldDescriptorType, Object> getAllFields() {
129     return Collections.unmodifiableMap(fields);
130   }
131 
132   /**
133    * Get an iterator to the field map.  This iterator should not be leaked
134    * out of the protobuf library as it is not protected from mutation.
135    */
iterator()136   public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
137     return fields.entrySet().iterator();
138   }
139 
140   /**
141    * Useful for implementing
142    * {@link Message#hasField(Descriptors.FieldDescriptor)}.
143    */
hasField(final FieldDescriptorType descriptor)144   public boolean hasField(final FieldDescriptorType descriptor) {
145     if (descriptor.isRepeated()) {
146       throw new IllegalArgumentException(
147         "hasField() can only be called on non-repeated fields.");
148     }
149 
150     return fields.get(descriptor) != null;
151   }
152 
153   /**
154    * Useful for implementing
155    * {@link Message#getField(Descriptors.FieldDescriptor)}.  This method
156    * returns {@code null} if the field is not set; in this case it is up
157    * to the caller to fetch the field's default value.
158    */
getField(final FieldDescriptorType descriptor)159   public Object getField(final FieldDescriptorType descriptor) {
160     return fields.get(descriptor);
161   }
162 
163   /**
164    * Useful for implementing
165    * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
166    */
167   @SuppressWarnings("unchecked")
setField(final FieldDescriptorType descriptor, Object value)168   public void setField(final FieldDescriptorType descriptor,
169                        Object value) {
170     if (descriptor.isRepeated()) {
171       if (!(value instanceof List)) {
172         throw new IllegalArgumentException(
173           "Wrong object type used with protocol message reflection.");
174       }
175 
176       // Wrap the contents in a new list so that the caller cannot change
177       // the list's contents after setting it.
178       final List newList = new ArrayList();
179       newList.addAll((List)value);
180       for (final Object element : newList) {
181         verifyType(descriptor.getLiteType(), element);
182       }
183       value = newList;
184     } else {
185       verifyType(descriptor.getLiteType(), value);
186     }
187 
188     fields.put(descriptor, value);
189   }
190 
191   /**
192    * Useful for implementing
193    * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
194    */
clearField(final FieldDescriptorType descriptor)195   public void clearField(final FieldDescriptorType descriptor) {
196     fields.remove(descriptor);
197   }
198 
199   /**
200    * Useful for implementing
201    * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
202    */
getRepeatedFieldCount(final FieldDescriptorType descriptor)203   public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
204     if (!descriptor.isRepeated()) {
205       throw new IllegalArgumentException(
206         "getRepeatedField() can only be called on repeated fields.");
207     }
208 
209     final Object value = fields.get(descriptor);
210     if (value == null) {
211       return 0;
212     } else {
213       return ((List) value).size();
214     }
215   }
216 
217   /**
218    * Useful for implementing
219    * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
220    */
getRepeatedField(final FieldDescriptorType descriptor, final int index)221   public Object getRepeatedField(final FieldDescriptorType descriptor,
222                                  final int index) {
223     if (!descriptor.isRepeated()) {
224       throw new IllegalArgumentException(
225         "getRepeatedField() can only be called on repeated fields.");
226     }
227 
228     final Object value = fields.get(descriptor);
229 
230     if (value == null) {
231       throw new IndexOutOfBoundsException();
232     } else {
233       return ((List) value).get(index);
234     }
235   }
236 
237   /**
238    * Useful for implementing
239    * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
240    */
241   @SuppressWarnings("unchecked")
setRepeatedField(final FieldDescriptorType descriptor, final int index, final Object value)242   public void setRepeatedField(final FieldDescriptorType descriptor,
243                                final int index,
244                                final Object value) {
245     if (!descriptor.isRepeated()) {
246       throw new IllegalArgumentException(
247         "getRepeatedField() can only be called on repeated fields.");
248     }
249 
250     final Object list = fields.get(descriptor);
251     if (list == null) {
252       throw new IndexOutOfBoundsException();
253     }
254 
255     verifyType(descriptor.getLiteType(), value);
256     ((List) list).set(index, value);
257   }
258 
259   /**
260    * Useful for implementing
261    * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
262    */
263   @SuppressWarnings("unchecked")
addRepeatedField(final FieldDescriptorType descriptor, final Object value)264   public void addRepeatedField(final FieldDescriptorType descriptor,
265                                final Object value) {
266     if (!descriptor.isRepeated()) {
267       throw new IllegalArgumentException(
268         "addRepeatedField() can only be called on repeated fields.");
269     }
270 
271     verifyType(descriptor.getLiteType(), value);
272 
273     final Object existingValue = fields.get(descriptor);
274     List list;
275     if (existingValue == null) {
276       list = new ArrayList();
277       fields.put(descriptor, list);
278     } else {
279       list = (List) existingValue;
280     }
281 
282     list.add(value);
283   }
284 
285   /**
286    * Verifies that the given object is of the correct type to be a valid
287    * value for the given field.  (For repeated fields, this checks if the
288    * object is the right type to be one element of the field.)
289    *
290    * @throws IllegalArgumentException The value is not of the right type.
291    */
verifyType(final WireFormat.FieldType type, final Object value)292   private static void verifyType(final WireFormat.FieldType type,
293                                  final Object value) {
294     if (value == null) {
295       throw new NullPointerException();
296     }
297 
298     boolean isValid = false;
299     switch (type.getJavaType()) {
300       case INT:          isValid = value instanceof Integer   ; break;
301       case LONG:         isValid = value instanceof Long      ; break;
302       case FLOAT:        isValid = value instanceof Float     ; break;
303       case DOUBLE:       isValid = value instanceof Double    ; break;
304       case BOOLEAN:      isValid = value instanceof Boolean   ; break;
305       case STRING:       isValid = value instanceof String    ; break;
306       case BYTE_STRING:  isValid = value instanceof ByteString; break;
307       case ENUM:
308         // TODO(kenton):  Caller must do type checking here, I guess.
309         isValid = value instanceof Internal.EnumLite;
310         break;
311       case MESSAGE:
312         // TODO(kenton):  Caller must do type checking here, I guess.
313         isValid = value instanceof MessageLite;
314         break;
315     }
316 
317     if (!isValid) {
318       // TODO(kenton):  When chaining calls to setField(), it can be hard to
319       //   tell from the stack trace which exact call failed, since the whole
320       //   chain is considered one line of code.  It would be nice to print
321       //   more information here, e.g. naming the field.  We used to do that.
322       //   But we can't now that FieldSet doesn't use descriptors.  Maybe this
323       //   isn't a big deal, though, since it would only really apply when using
324       //   reflection and generally people don't chain reflection setters.
325       throw new IllegalArgumentException(
326         "Wrong object type used with protocol message reflection.");
327     }
328   }
329 
330   // =================================================================
331   // Parsing and serialization
332 
333   /**
334    * See {@link Message#isInitialized()}.  Note:  Since {@code FieldSet}
335    * itself does not have any way of knowing about required fields that
336    * aren't actually present in the set, it is up to the caller to check
337    * that all required fields are present.
338    */
339   @SuppressWarnings("unchecked")
isInitialized()340   public boolean isInitialized() {
341     for (final Map.Entry<FieldDescriptorType, Object> entry:
342          fields.entrySet()) {
343       final FieldDescriptorType descriptor = entry.getKey();
344       if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
345         if (descriptor.isRepeated()) {
346           for (final MessageLite element:
347                (List<MessageLite>) entry.getValue()) {
348             if (!element.isInitialized()) {
349               return false;
350             }
351           }
352         } else {
353           if (!((MessageLite) entry.getValue()).isInitialized()) {
354             return false;
355           }
356         }
357       }
358     }
359 
360     return true;
361   }
362 
363   /**
364    * Given a field type, return the wire type.
365    *
366    * @returns One of the {@code WIRETYPE_} constants defined in
367    *          {@link WireFormat}.
368    */
getWireFormatForFieldType(final WireFormat.FieldType type, boolean isPacked)369   static int getWireFormatForFieldType(final WireFormat.FieldType type,
370                                        boolean isPacked) {
371     if (isPacked) {
372       return WireFormat.WIRETYPE_LENGTH_DELIMITED;
373     } else {
374       return type.getWireType();
375     }
376   }
377 
378   /**
379    * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
380    */
381   @SuppressWarnings("unchecked")
mergeFrom(final FieldSet<FieldDescriptorType> other)382   public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
383     for (final Map.Entry<FieldDescriptorType, Object> entry:
384          other.fields.entrySet()) {
385       final FieldDescriptorType descriptor = entry.getKey();
386       final Object otherValue = entry.getValue();
387 
388       if (descriptor.isRepeated()) {
389         Object value = fields.get(descriptor);
390         if (value == null) {
391           // Our list is empty, but we still need to make a defensive copy of
392           // the other list since we don't know if the other FieldSet is still
393           // mutable.
394           fields.put(descriptor, new ArrayList((List) otherValue));
395         } else {
396           // Concatenate the lists.
397           ((List) value).addAll((List) otherValue);
398         }
399       } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
400         Object value = fields.get(descriptor);
401         if (value == null) {
402           fields.put(descriptor, otherValue);
403         } else {
404           // Merge the messages.
405           fields.put(descriptor,
406               descriptor.internalMergeFrom(
407                 ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
408               .build());
409         }
410 
411       } else {
412         fields.put(descriptor, otherValue);
413       }
414     }
415   }
416 
417   // TODO(kenton):  Move static parsing and serialization methods into some
418   //   other class.  Probably WireFormat.
419 
420   /**
421    * Read a field of any primitive type from a CodedInputStream.  Enums,
422    * groups, and embedded messages are not handled by this method.
423    *
424    * @param input The stream from which to read.
425    * @param type Declared type of the field.
426    * @return An object representing the field's value, of the exact
427    *         type which would be returned by
428    *         {@link Message#getField(Descriptors.FieldDescriptor)} for
429    *         this field.
430    */
readPrimitiveField( CodedInputStream input, final WireFormat.FieldType type)431   public static Object readPrimitiveField(
432       CodedInputStream input,
433       final WireFormat.FieldType type) throws IOException {
434     switch (type) {
435       case DOUBLE  : return input.readDouble  ();
436       case FLOAT   : return input.readFloat   ();
437       case INT64   : return input.readInt64   ();
438       case UINT64  : return input.readUInt64  ();
439       case INT32   : return input.readInt32   ();
440       case FIXED64 : return input.readFixed64 ();
441       case FIXED32 : return input.readFixed32 ();
442       case BOOL    : return input.readBool    ();
443       case STRING  : return input.readString  ();
444       case BYTES   : return input.readBytes   ();
445       case UINT32  : return input.readUInt32  ();
446       case SFIXED32: return input.readSFixed32();
447       case SFIXED64: return input.readSFixed64();
448       case SINT32  : return input.readSInt32  ();
449       case SINT64  : return input.readSInt64  ();
450 
451       case GROUP:
452         throw new IllegalArgumentException(
453           "readPrimitiveField() cannot handle nested groups.");
454       case MESSAGE:
455         throw new IllegalArgumentException(
456           "readPrimitiveField() cannot handle embedded messages.");
457       case ENUM:
458         // We don't handle enums because we don't know what to do if the
459         // value is not recognized.
460         throw new IllegalArgumentException(
461           "readPrimitiveField() cannot handle enums.");
462     }
463 
464     throw new RuntimeException(
465       "There is no way to get here, but the compiler thinks otherwise.");
466   }
467 
468   /** See {@link Message#writeTo(CodedOutputStream)}. */
writeTo(final CodedOutputStream output)469   public void writeTo(final CodedOutputStream output)
470                       throws IOException {
471     for (final Map.Entry<FieldDescriptorType, Object> entry:
472          fields.entrySet()) {
473       writeField(entry.getKey(), entry.getValue(), output);
474     }
475   }
476 
477   /**
478    * Like {@link #writeTo} but uses MessageSet wire format.
479    */
writeMessageSetTo(final CodedOutputStream output)480   public void writeMessageSetTo(final CodedOutputStream output)
481                                 throws IOException {
482     for (final Map.Entry<FieldDescriptorType, Object> entry:
483          fields.entrySet()) {
484       final FieldDescriptorType descriptor = entry.getKey();
485       if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
486           !descriptor.isRepeated() && !descriptor.isPacked()) {
487         output.writeMessageSetExtension(entry.getKey().getNumber(),
488                                         (MessageLite) entry.getValue());
489       } else {
490         writeField(descriptor, entry.getValue(), output);
491       }
492     }
493   }
494 
495   /**
496    * Write a single tag-value pair to the stream.
497    *
498    * @param output The output stream.
499    * @param type   The field's type.
500    * @param number The field's number.
501    * @param value  Object representing the field's value.  Must be of the exact
502    *               type which would be returned by
503    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
504    *               this field.
505    */
writeElement(final CodedOutputStream output, final WireFormat.FieldType type, final int number, final Object value)506   private static void writeElement(final CodedOutputStream output,
507                                    final WireFormat.FieldType type,
508                                    final int number,
509                                    final Object value) throws IOException {
510     // Special case for groups, which need a start and end tag; other fields
511     // can just use writeTag() and writeFieldNoTag().
512     if (type == WireFormat.FieldType.GROUP) {
513       output.writeGroup(number, (MessageLite) value);
514     } else {
515       output.writeTag(number, getWireFormatForFieldType(type, false));
516       writeElementNoTag(output, type, value);
517     }
518   }
519 
520   /**
521    * Write a field of arbitrary type, without its tag, to the stream.
522    *
523    * @param output The output stream.
524    * @param type The field's type.
525    * @param value  Object representing the field's value.  Must be of the exact
526    *               type which would be returned by
527    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
528    *               this field.
529    */
writeElementNoTag( final CodedOutputStream output, final WireFormat.FieldType type, final Object value)530   private static void writeElementNoTag(
531       final CodedOutputStream output,
532       final WireFormat.FieldType type,
533       final Object value) throws IOException {
534     switch (type) {
535       case DOUBLE  : output.writeDoubleNoTag  ((Double     ) value); break;
536       case FLOAT   : output.writeFloatNoTag   ((Float      ) value); break;
537       case INT64   : output.writeInt64NoTag   ((Long       ) value); break;
538       case UINT64  : output.writeUInt64NoTag  ((Long       ) value); break;
539       case INT32   : output.writeInt32NoTag   ((Integer    ) value); break;
540       case FIXED64 : output.writeFixed64NoTag ((Long       ) value); break;
541       case FIXED32 : output.writeFixed32NoTag ((Integer    ) value); break;
542       case BOOL    : output.writeBoolNoTag    ((Boolean    ) value); break;
543       case STRING  : output.writeStringNoTag  ((String     ) value); break;
544       case GROUP   : output.writeGroupNoTag   ((MessageLite) value); break;
545       case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
546       case BYTES   : output.writeBytesNoTag   ((ByteString ) value); break;
547       case UINT32  : output.writeUInt32NoTag  ((Integer    ) value); break;
548       case SFIXED32: output.writeSFixed32NoTag((Integer    ) value); break;
549       case SFIXED64: output.writeSFixed64NoTag((Long       ) value); break;
550       case SINT32  : output.writeSInt32NoTag  ((Integer    ) value); break;
551       case SINT64  : output.writeSInt64NoTag  ((Long       ) value); break;
552 
553       case ENUM:
554         output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
555         break;
556     }
557   }
558 
559   /** Write a single field. */
writeField(final FieldDescriptorLite<?> descriptor, final Object value, final CodedOutputStream output)560   public static void writeField(final FieldDescriptorLite<?> descriptor,
561                                 final Object value,
562                                 final CodedOutputStream output)
563                                 throws IOException {
564     WireFormat.FieldType type = descriptor.getLiteType();
565     int number = descriptor.getNumber();
566     if (descriptor.isRepeated()) {
567       final List valueList = (List)value;
568       if (descriptor.isPacked()) {
569         output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
570         // Compute the total data size so the length can be written.
571         int dataSize = 0;
572         for (final Object element : valueList) {
573           dataSize += computeElementSizeNoTag(type, element);
574         }
575         output.writeRawVarint32(dataSize);
576         // Write the data itself, without any tags.
577         for (final Object element : valueList) {
578           writeElementNoTag(output, type, element);
579         }
580       } else {
581         for (final Object element : valueList) {
582           writeElement(output, type, number, element);
583         }
584       }
585     } else {
586       writeElement(output, type, number, value);
587     }
588   }
589 
590   /**
591    * See {@link Message#getSerializedSize()}.  It's up to the caller to cache
592    * the resulting size if desired.
593    */
getSerializedSize()594   public int getSerializedSize() {
595     int size = 0;
596     for (final Map.Entry<FieldDescriptorType, Object> entry:
597          fields.entrySet()) {
598       size += computeFieldSize(entry.getKey(), entry.getValue());
599     }
600     return size;
601   }
602 
603   /**
604    * Like {@link #getSerializedSize} but uses MessageSet wire format.
605    */
getMessageSetSerializedSize()606   public int getMessageSetSerializedSize() {
607     int size = 0;
608     for (final Map.Entry<FieldDescriptorType, Object> entry:
609          fields.entrySet()) {
610       final FieldDescriptorType descriptor = entry.getKey();
611       if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
612           !descriptor.isRepeated() && !descriptor.isPacked()) {
613         size += CodedOutputStream.computeMessageSetExtensionSize(
614                   entry.getKey().getNumber(), (MessageLite) entry.getValue());
615       } else {
616         size += computeFieldSize(descriptor, entry.getValue());
617       }
618     }
619     return size;
620   }
621 
622   /**
623    * Compute the number of bytes that would be needed to encode a
624    * single tag/value pair of arbitrary type.
625    *
626    * @param type   The field's type.
627    * @param number The field's number.
628    * @param value  Object representing the field's value.  Must be of the exact
629    *               type which would be returned by
630    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
631    *               this field.
632    */
computeElementSize( final WireFormat.FieldType type, final int number, final Object value)633   private static int computeElementSize(
634       final WireFormat.FieldType type,
635       final int number, final Object value) {
636     int tagSize = CodedOutputStream.computeTagSize(number);
637     if (type == WireFormat.FieldType.GROUP) {
638       tagSize *= 2;
639     }
640     return tagSize + computeElementSizeNoTag(type, value);
641   }
642 
643   /**
644    * Compute the number of bytes that would be needed to encode a
645    * particular value of arbitrary type, excluding tag.
646    *
647    * @param type   The field's type.
648    * @param value  Object representing the field's value.  Must be of the exact
649    *               type which would be returned by
650    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
651    *               this field.
652    */
computeElementSizeNoTag( final WireFormat.FieldType type, final Object value)653   private static int computeElementSizeNoTag(
654       final WireFormat.FieldType type, final Object value) {
655     switch (type) {
656       // Note:  Minor violation of 80-char limit rule here because this would
657       //   actually be harder to read if we wrapped the lines.
658       case DOUBLE  : return CodedOutputStream.computeDoubleSizeNoTag  ((Double     )value);
659       case FLOAT   : return CodedOutputStream.computeFloatSizeNoTag   ((Float      )value);
660       case INT64   : return CodedOutputStream.computeInt64SizeNoTag   ((Long       )value);
661       case UINT64  : return CodedOutputStream.computeUInt64SizeNoTag  ((Long       )value);
662       case INT32   : return CodedOutputStream.computeInt32SizeNoTag   ((Integer    )value);
663       case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long       )value);
664       case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer    )value);
665       case BOOL    : return CodedOutputStream.computeBoolSizeNoTag    ((Boolean    )value);
666       case STRING  : return CodedOutputStream.computeStringSizeNoTag  ((String     )value);
667       case GROUP   : return CodedOutputStream.computeGroupSizeNoTag   ((MessageLite)value);
668       case MESSAGE : return CodedOutputStream.computeMessageSizeNoTag ((MessageLite)value);
669       case BYTES   : return CodedOutputStream.computeBytesSizeNoTag   ((ByteString )value);
670       case UINT32  : return CodedOutputStream.computeUInt32SizeNoTag  ((Integer    )value);
671       case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer    )value);
672       case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long       )value);
673       case SINT32  : return CodedOutputStream.computeSInt32SizeNoTag  ((Integer    )value);
674       case SINT64  : return CodedOutputStream.computeSInt64SizeNoTag  ((Long       )value);
675 
676       case ENUM:
677         return CodedOutputStream.computeEnumSizeNoTag(
678             ((Internal.EnumLite) value).getNumber());
679     }
680 
681     throw new RuntimeException(
682       "There is no way to get here, but the compiler thinks otherwise.");
683   }
684 
685   /**
686    * Compute the number of bytes needed to encode a particular field.
687    */
computeFieldSize(final FieldDescriptorLite<?> descriptor, final Object value)688   public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
689                                      final Object value) {
690     WireFormat.FieldType type = descriptor.getLiteType();
691     int number = descriptor.getNumber();
692     if (descriptor.isRepeated()) {
693       if (descriptor.isPacked()) {
694         int dataSize = 0;
695         for (final Object element : (List)value) {
696           dataSize += computeElementSizeNoTag(type, element);
697         }
698         return dataSize +
699             CodedOutputStream.computeTagSize(number) +
700             CodedOutputStream.computeRawVarint32Size(dataSize);
701       } else {
702         int size = 0;
703         for (final Object element : (List)value) {
704           size += computeElementSize(type, number, element);
705         }
706         return size;
707       }
708     } else {
709       return computeElementSize(type, number, value);
710     }
711   }
712 }
713