• 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 com.google.protobuf.LazyField.LazyIterator;
34 
35 import java.io.IOException;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Map;
41 
42 /**
43  * A class which represents an arbitrary set of fields of some message type.
44  * This is used to implement {@link DynamicMessage}, and also to represent
45  * extensions in {@link GeneratedMessage}.  This class is package-private,
46  * since outside users should probably be using {@link DynamicMessage}.
47  *
48  * @author kenton@google.com Kenton Varda
49  */
50 final class FieldSet<FieldDescriptorType extends
51       FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
52   /**
53    * Interface for a FieldDescriptor or lite extension descriptor.  This
54    * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
55    */
56   public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
57       extends Comparable<T> {
getNumber()58     int getNumber();
getLiteType()59     WireFormat.FieldType getLiteType();
getLiteJavaType()60     WireFormat.JavaType getLiteJavaType();
isRepeated()61     boolean isRepeated();
isPacked()62     boolean isPacked();
getEnumType()63     Internal.EnumLiteMap<?> getEnumType();
64 
65     // If getLiteJavaType() == MESSAGE, this merges a message object of the
66     // type into a builder of the type.  Returns {@code to}.
internalMergeFrom( MessageLite.Builder to, MessageLite from)67     MessageLite.Builder internalMergeFrom(
68         MessageLite.Builder to, MessageLite from);
69   }
70 
71   private final SmallSortedMap<FieldDescriptorType, Object> fields;
72   private boolean isImmutable;
73   private boolean hasLazyField = false;
74 
75   /** Construct a new FieldSet. */
FieldSet()76   private FieldSet() {
77     this.fields = SmallSortedMap.newFieldMap(16);
78   }
79 
80   /**
81    * Construct an empty FieldSet.  This is only used to initialize
82    * DEFAULT_INSTANCE.
83    */
FieldSet(final boolean dummy)84   private FieldSet(final boolean dummy) {
85     this.fields = SmallSortedMap.newFieldMap(0);
86     makeImmutable();
87   }
88 
89   /** Construct a new FieldSet. */
90   public static <T extends FieldSet.FieldDescriptorLite<T>>
newFieldSet()91       FieldSet<T> newFieldSet() {
92     return new FieldSet<T>();
93   }
94 
95   /** Get an immutable empty FieldSet. */
96   @SuppressWarnings("unchecked")
97   public static <T extends FieldSet.FieldDescriptorLite<T>>
emptySet()98       FieldSet<T> emptySet() {
99     return DEFAULT_INSTANCE;
100   }
101   @SuppressWarnings("rawtypes")
102   private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
103 
104   /** Make this FieldSet immutable from this point forward. */
105   @SuppressWarnings("unchecked")
makeImmutable()106   public void makeImmutable() {
107     if (isImmutable) {
108       return;
109     }
110     fields.makeImmutable();
111     isImmutable = true;
112   }
113 
114   /**
115    * Returns whether the FieldSet is immutable. This is true if it is the
116    * {@link #emptySet} or if {@link #makeImmutable} were called.
117    *
118    * @return whether the FieldSet is immutable.
119    */
isImmutable()120   public boolean isImmutable() {
121     return isImmutable;
122   }
123 
124   @Override
equals(Object o)125   public boolean equals(Object o) {
126     if (this == o) {
127       return true;
128     }
129 
130     if (!(o instanceof FieldSet)) {
131       return false;
132     }
133 
134     FieldSet<?> other = (FieldSet<?>) o;
135     return fields.equals(other.fields);
136   }
137 
138   @Override
hashCode()139   public int hashCode() {
140     return fields.hashCode();
141   }
142 
143   /**
144    * Clones the FieldSet. The returned FieldSet will be mutable even if the
145    * original FieldSet was immutable.
146    *
147    * @return the newly cloned FieldSet
148    */
149   @Override
clone()150   public FieldSet<FieldDescriptorType> clone() {
151     // We can't just call fields.clone because List objects in the map
152     // should not be shared.
153     FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
154     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
155       Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
156       FieldDescriptorType descriptor = entry.getKey();
157       clone.setField(descriptor, entry.getValue());
158     }
159     for (Map.Entry<FieldDescriptorType, Object> entry :
160              fields.getOverflowEntries()) {
161       FieldDescriptorType descriptor = entry.getKey();
162       clone.setField(descriptor, entry.getValue());
163     }
164     clone.hasLazyField = hasLazyField;
165     return clone;
166   }
167 
168 
169   // =================================================================
170 
171   /** See {@link Message.Builder#clear()}. */
clear()172   public void clear() {
173     fields.clear();
174     hasLazyField = false;
175   }
176 
177   /**
178    * Get a simple map containing all the fields.
179    */
getAllFields()180   public Map<FieldDescriptorType, Object> getAllFields() {
181     if (hasLazyField) {
182       SmallSortedMap<FieldDescriptorType, Object> result =
183           SmallSortedMap.newFieldMap(16);
184       for (int i = 0; i < fields.getNumArrayEntries(); i++) {
185         cloneFieldEntry(result, fields.getArrayEntryAt(i));
186       }
187       for (Map.Entry<FieldDescriptorType, Object> entry :
188           fields.getOverflowEntries()) {
189         cloneFieldEntry(result, entry);
190       }
191       if (fields.isImmutable()) {
192         result.makeImmutable();
193       }
194       return result;
195     }
196     return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
197   }
198 
cloneFieldEntry(Map<FieldDescriptorType, Object> map, Map.Entry<FieldDescriptorType, Object> entry)199   private void cloneFieldEntry(Map<FieldDescriptorType, Object> map,
200       Map.Entry<FieldDescriptorType, Object> entry) {
201     FieldDescriptorType key = entry.getKey();
202     Object value = entry.getValue();
203     if (value instanceof LazyField) {
204       map.put(key, ((LazyField) value).getValue());
205     } else {
206       map.put(key, value);
207     }
208   }
209 
210   /**
211    * Get an iterator to the field map. This iterator should not be leaked out
212    * of the protobuf library as it is not protected from mutation when fields
213    * is not immutable.
214    */
iterator()215   public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
216     if (hasLazyField) {
217       return new LazyIterator<FieldDescriptorType>(
218           fields.entrySet().iterator());
219     }
220     return fields.entrySet().iterator();
221   }
222 
223   /**
224    * Useful for implementing
225    * {@link Message#hasField(Descriptors.FieldDescriptor)}.
226    */
hasField(final FieldDescriptorType descriptor)227   public boolean hasField(final FieldDescriptorType descriptor) {
228     if (descriptor.isRepeated()) {
229       throw new IllegalArgumentException(
230         "hasField() can only be called on non-repeated fields.");
231     }
232 
233     return fields.get(descriptor) != null;
234   }
235 
236   /**
237    * Useful for implementing
238    * {@link Message#getField(Descriptors.FieldDescriptor)}.  This method
239    * returns {@code null} if the field is not set; in this case it is up
240    * to the caller to fetch the field's default value.
241    */
getField(final FieldDescriptorType descriptor)242   public Object getField(final FieldDescriptorType descriptor) {
243     Object o = fields.get(descriptor);
244     if (o instanceof LazyField) {
245       return ((LazyField) o).getValue();
246     }
247     return o;
248   }
249 
250   /**
251    * Useful for implementing
252    * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
253    */
254   @SuppressWarnings({"unchecked", "rawtypes"})
setField(final FieldDescriptorType descriptor, Object value)255   public void setField(final FieldDescriptorType descriptor,
256                        Object value) {
257     if (descriptor.isRepeated()) {
258       if (!(value instanceof List)) {
259         throw new IllegalArgumentException(
260           "Wrong object type used with protocol message reflection.");
261       }
262 
263       // Wrap the contents in a new list so that the caller cannot change
264       // the list's contents after setting it.
265       final List newList = new ArrayList();
266       newList.addAll((List) value);
267       for (final Object element : newList) {
268         verifyType(descriptor.getLiteType(), element);
269       }
270       value = newList;
271     } else {
272       verifyType(descriptor.getLiteType(), value);
273     }
274 
275     if (value instanceof LazyField) {
276       hasLazyField = true;
277     }
278     fields.put(descriptor, value);
279   }
280 
281   /**
282    * Useful for implementing
283    * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
284    */
clearField(final FieldDescriptorType descriptor)285   public void clearField(final FieldDescriptorType descriptor) {
286     fields.remove(descriptor);
287     if (fields.isEmpty()) {
288       hasLazyField = false;
289     }
290   }
291 
292   /**
293    * Useful for implementing
294    * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
295    */
getRepeatedFieldCount(final FieldDescriptorType descriptor)296   public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
297     if (!descriptor.isRepeated()) {
298       throw new IllegalArgumentException(
299         "getRepeatedField() can only be called on repeated fields.");
300     }
301 
302     final Object value = getField(descriptor);
303     if (value == null) {
304       return 0;
305     } else {
306       return ((List<?>) value).size();
307     }
308   }
309 
310   /**
311    * Useful for implementing
312    * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
313    */
getRepeatedField(final FieldDescriptorType descriptor, final int index)314   public Object getRepeatedField(final FieldDescriptorType descriptor,
315                                  final int index) {
316     if (!descriptor.isRepeated()) {
317       throw new IllegalArgumentException(
318         "getRepeatedField() can only be called on repeated fields.");
319     }
320 
321     final Object value = getField(descriptor);
322 
323     if (value == null) {
324       throw new IndexOutOfBoundsException();
325     } else {
326       return ((List<?>) value).get(index);
327     }
328   }
329 
330   /**
331    * Useful for implementing
332    * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
333    */
334   @SuppressWarnings("unchecked")
setRepeatedField(final FieldDescriptorType descriptor, final int index, final Object value)335   public void setRepeatedField(final FieldDescriptorType descriptor,
336                                final int index,
337                                final Object value) {
338     if (!descriptor.isRepeated()) {
339       throw new IllegalArgumentException(
340         "getRepeatedField() can only be called on repeated fields.");
341     }
342 
343     final Object list = getField(descriptor);
344     if (list == null) {
345       throw new IndexOutOfBoundsException();
346     }
347 
348     verifyType(descriptor.getLiteType(), value);
349     ((List<Object>) list).set(index, value);
350   }
351 
352   /**
353    * Useful for implementing
354    * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
355    */
356   @SuppressWarnings("unchecked")
addRepeatedField(final FieldDescriptorType descriptor, final Object value)357   public void addRepeatedField(final FieldDescriptorType descriptor,
358                                final Object value) {
359     if (!descriptor.isRepeated()) {
360       throw new IllegalArgumentException(
361         "addRepeatedField() can only be called on repeated fields.");
362     }
363 
364     verifyType(descriptor.getLiteType(), value);
365 
366     final Object existingValue = getField(descriptor);
367     List<Object> list;
368     if (existingValue == null) {
369       list = new ArrayList<Object>();
370       fields.put(descriptor, list);
371     } else {
372       list = (List<Object>) existingValue;
373     }
374 
375     list.add(value);
376   }
377 
378   /**
379    * Verifies that the given object is of the correct type to be a valid
380    * value for the given field.  (For repeated fields, this checks if the
381    * object is the right type to be one element of the field.)
382    *
383    * @throws IllegalArgumentException The value is not of the right type.
384    */
verifyType(final WireFormat.FieldType type, final Object value)385   private static void verifyType(final WireFormat.FieldType type,
386                                  final Object value) {
387     if (value == null) {
388       throw new NullPointerException();
389     }
390 
391     boolean isValid = false;
392     switch (type.getJavaType()) {
393       case INT:          isValid = value instanceof Integer   ; break;
394       case LONG:         isValid = value instanceof Long      ; break;
395       case FLOAT:        isValid = value instanceof Float     ; break;
396       case DOUBLE:       isValid = value instanceof Double    ; break;
397       case BOOLEAN:      isValid = value instanceof Boolean   ; break;
398       case STRING:       isValid = value instanceof String    ; break;
399       case BYTE_STRING:
400         isValid = value instanceof ByteString || value instanceof byte[];
401         break;
402       case ENUM:
403         // TODO(kenton):  Caller must do type checking here, I guess.
404         isValid =
405             (value instanceof Integer || value instanceof Internal.EnumLite);
406         break;
407       case MESSAGE:
408         // TODO(kenton):  Caller must do type checking here, I guess.
409         isValid =
410             (value instanceof MessageLite) || (value instanceof LazyField);
411         break;
412     }
413 
414     if (!isValid) {
415       // TODO(kenton):  When chaining calls to setField(), it can be hard to
416       //   tell from the stack trace which exact call failed, since the whole
417       //   chain is considered one line of code.  It would be nice to print
418       //   more information here, e.g. naming the field.  We used to do that.
419       //   But we can't now that FieldSet doesn't use descriptors.  Maybe this
420       //   isn't a big deal, though, since it would only really apply when using
421       //   reflection and generally people don't chain reflection setters.
422       throw new IllegalArgumentException(
423         "Wrong object type used with protocol message reflection.");
424     }
425   }
426 
427   // =================================================================
428   // Parsing and serialization
429 
430   /**
431    * See {@link Message#isInitialized()}.  Note:  Since {@code FieldSet}
432    * itself does not have any way of knowing about required fields that
433    * aren't actually present in the set, it is up to the caller to check
434    * that all required fields are present.
435    */
isInitialized()436   public boolean isInitialized() {
437     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
438       if (!isInitialized(fields.getArrayEntryAt(i))) {
439         return false;
440       }
441     }
442     for (final Map.Entry<FieldDescriptorType, Object> entry :
443              fields.getOverflowEntries()) {
444       if (!isInitialized(entry)) {
445         return false;
446       }
447     }
448     return true;
449   }
450 
451   @SuppressWarnings("unchecked")
isInitialized( final Map.Entry<FieldDescriptorType, Object> entry)452   private boolean isInitialized(
453       final Map.Entry<FieldDescriptorType, Object> entry) {
454     final FieldDescriptorType descriptor = entry.getKey();
455     if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
456       if (descriptor.isRepeated()) {
457         for (final MessageLite element:
458                  (List<MessageLite>) entry.getValue()) {
459           if (!element.isInitialized()) {
460             return false;
461           }
462         }
463       } else {
464         Object value = entry.getValue();
465         if (value instanceof MessageLite) {
466           if (!((MessageLite) value).isInitialized()) {
467             return false;
468           }
469         } else if (value instanceof LazyField) {
470           return true;
471         } else {
472           throw new IllegalArgumentException(
473               "Wrong object type used with protocol message reflection.");
474         }
475       }
476     }
477     return true;
478   }
479 
480   /**
481    * Given a field type, return the wire type.
482    *
483    * @returns One of the {@code WIRETYPE_} constants defined in
484    *          {@link WireFormat}.
485    */
getWireFormatForFieldType(final WireFormat.FieldType type, boolean isPacked)486   static int getWireFormatForFieldType(final WireFormat.FieldType type,
487                                        boolean isPacked) {
488     if (isPacked) {
489       return WireFormat.WIRETYPE_LENGTH_DELIMITED;
490     } else {
491       return type.getWireType();
492     }
493   }
494 
495   /**
496    * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
497    * {@link FieldSet}.
498    */
mergeFrom(final FieldSet<FieldDescriptorType> other)499   public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
500     for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
501       mergeFromField(other.fields.getArrayEntryAt(i));
502     }
503     for (final Map.Entry<FieldDescriptorType, Object> entry :
504              other.fields.getOverflowEntries()) {
505       mergeFromField(entry);
506     }
507   }
508 
cloneIfMutable(Object value)509   private Object cloneIfMutable(Object value) {
510     if (value instanceof byte[]) {
511       byte[] bytes = (byte[]) value;
512       byte[] copy = new byte[bytes.length];
513       System.arraycopy(bytes, 0, copy, 0, bytes.length);
514       return copy;
515     } else {
516       return value;
517     }
518   }
519 
520   @SuppressWarnings({"unchecked", "rawtypes"})
mergeFromField( final Map.Entry<FieldDescriptorType, Object> entry)521   private void mergeFromField(
522       final Map.Entry<FieldDescriptorType, Object> entry) {
523     final FieldDescriptorType descriptor = entry.getKey();
524     Object otherValue = entry.getValue();
525     if (otherValue instanceof LazyField) {
526       otherValue = ((LazyField) otherValue).getValue();
527     }
528 
529     if (descriptor.isRepeated()) {
530       Object value = getField(descriptor);
531       if (value == null) {
532         value = new ArrayList();
533       }
534       for (Object element : (List) otherValue) {
535         ((List) value).add(cloneIfMutable(element));
536       }
537       fields.put(descriptor, value);
538     } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
539       Object value = getField(descriptor);
540       if (value == null) {
541         fields.put(descriptor, cloneIfMutable(otherValue));
542       } else {
543         // Merge the messages.
544           value = descriptor.internalMergeFrom(
545                 ((MessageLite) value).toBuilder(), (MessageLite) otherValue)
546                 .build();
547 
548         fields.put(descriptor, value);
549       }
550     } else {
551       fields.put(descriptor, cloneIfMutable(otherValue));
552     }
553   }
554 
555   // TODO(kenton):  Move static parsing and serialization methods into some
556   //   other class.  Probably WireFormat.
557 
558   /**
559    * Read a field of any primitive type for immutable messages from a
560    * CodedInputStream. Enums, groups, and embedded messages are not handled by
561    * this method.
562    *
563    * @param input The stream from which to read.
564    * @param type Declared type of the field.
565    * @param checkUtf8 When true, check that the input is valid utf8.
566    * @return An object representing the field's value, of the exact
567    *         type which would be returned by
568    *         {@link Message#getField(Descriptors.FieldDescriptor)} for
569    *         this field.
570    */
readPrimitiveField( CodedInputStream input, final WireFormat.FieldType type, boolean checkUtf8)571   public static Object readPrimitiveField(
572       CodedInputStream input,
573       final WireFormat.FieldType type,
574       boolean checkUtf8) throws IOException {
575     if (checkUtf8) {
576       return WireFormat.readPrimitiveField(input, type,
577           WireFormat.Utf8Validation.STRICT);
578     } else {
579       return WireFormat.readPrimitiveField(input, type,
580           WireFormat.Utf8Validation.LOOSE);
581     }
582   }
583 
584 
585   /** See {@link Message#writeTo(CodedOutputStream)}. */
writeTo(final CodedOutputStream output)586   public void writeTo(final CodedOutputStream output)
587                       throws IOException {
588     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
589       final Map.Entry<FieldDescriptorType, Object> entry =
590           fields.getArrayEntryAt(i);
591       writeField(entry.getKey(), entry.getValue(), output);
592     }
593     for (final Map.Entry<FieldDescriptorType, Object> entry :
594          fields.getOverflowEntries()) {
595       writeField(entry.getKey(), entry.getValue(), output);
596     }
597   }
598 
599   /**
600    * Like {@link #writeTo} but uses MessageSet wire format.
601    */
writeMessageSetTo(final CodedOutputStream output)602   public void writeMessageSetTo(final CodedOutputStream output)
603                                 throws IOException {
604     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
605       writeMessageSetTo(fields.getArrayEntryAt(i), output);
606     }
607     for (final Map.Entry<FieldDescriptorType, Object> entry :
608              fields.getOverflowEntries()) {
609       writeMessageSetTo(entry, output);
610     }
611   }
612 
writeMessageSetTo( final Map.Entry<FieldDescriptorType, Object> entry, final CodedOutputStream output)613   private void writeMessageSetTo(
614       final Map.Entry<FieldDescriptorType, Object> entry,
615       final CodedOutputStream output) throws IOException {
616     final FieldDescriptorType descriptor = entry.getKey();
617     if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
618         !descriptor.isRepeated() && !descriptor.isPacked()) {
619       Object value = entry.getValue();
620       if (value instanceof LazyField) {
621         value = ((LazyField) value).getValue();
622       }
623       output.writeMessageSetExtension(entry.getKey().getNumber(),
624                                       (MessageLite) value);
625     } else {
626       writeField(descriptor, entry.getValue(), output);
627     }
628   }
629 
630   /**
631    * Write a single tag-value pair to the stream.
632    *
633    * @param output The output stream.
634    * @param type   The field's type.
635    * @param number The field's number.
636    * @param value  Object representing the field's value.  Must be of the exact
637    *               type which would be returned by
638    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
639    *               this field.
640    */
writeElement( final CodedOutputStream output, final WireFormat.FieldType type, final int number, final Object value)641   static void writeElement(
642       final CodedOutputStream output,
643       final WireFormat.FieldType type,
644       final int number,
645       final Object value) throws IOException {
646     // Special case for groups, which need a start and end tag; other fields
647     // can just use writeTag() and writeFieldNoTag().
648     if (type == WireFormat.FieldType.GROUP) {
649         output.writeGroup(number, (MessageLite) value);
650     } else {
651       output.writeTag(number, getWireFormatForFieldType(type, false));
652       writeElementNoTag(output, type, value);
653     }
654   }
655 
656   /**
657    * Write a field of arbitrary type, without its tag, to the stream.
658    *
659    * @param output The output stream.
660    * @param type The field's type.
661    * @param value  Object representing the field's value.  Must be of the exact
662    *               type which would be returned by
663    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
664    *               this field.
665    */
writeElementNoTag( final CodedOutputStream output, final WireFormat.FieldType type, final Object value)666   static void writeElementNoTag(
667       final CodedOutputStream output,
668       final WireFormat.FieldType type,
669       final Object value) throws IOException {
670     switch (type) {
671       case DOUBLE  : output.writeDoubleNoTag  ((Double     ) value); break;
672       case FLOAT   : output.writeFloatNoTag   ((Float      ) value); break;
673       case INT64   : output.writeInt64NoTag   ((Long       ) value); break;
674       case UINT64  : output.writeUInt64NoTag  ((Long       ) value); break;
675       case INT32   : output.writeInt32NoTag   ((Integer    ) value); break;
676       case FIXED64 : output.writeFixed64NoTag ((Long       ) value); break;
677       case FIXED32 : output.writeFixed32NoTag ((Integer    ) value); break;
678       case BOOL    : output.writeBoolNoTag    ((Boolean    ) value); break;
679       case GROUP   : output.writeGroupNoTag   ((MessageLite) value); break;
680       case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
681       case STRING:
682         if (value instanceof ByteString) {
683           output.writeBytesNoTag((ByteString) value);
684         } else {
685           output.writeStringNoTag((String) value);
686         }
687         break;
688       case BYTES:
689         if (value instanceof ByteString) {
690           output.writeBytesNoTag((ByteString) value);
691         } else {
692           output.writeByteArrayNoTag((byte[]) value);
693         }
694         break;
695       case UINT32  : output.writeUInt32NoTag  ((Integer    ) value); break;
696       case SFIXED32: output.writeSFixed32NoTag((Integer    ) value); break;
697       case SFIXED64: output.writeSFixed64NoTag((Long       ) value); break;
698       case SINT32  : output.writeSInt32NoTag  ((Integer    ) value); break;
699       case SINT64  : output.writeSInt64NoTag  ((Long       ) value); break;
700 
701       case ENUM:
702         if (value instanceof Internal.EnumLite) {
703           output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
704         } else {
705           output.writeEnumNoTag(((Integer) value).intValue());
706         }
707         break;
708     }
709   }
710 
711   /** Write a single field. */
writeField(final FieldDescriptorLite<?> descriptor, final Object value, final CodedOutputStream output)712   public static void writeField(final FieldDescriptorLite<?> descriptor,
713                                 final Object value,
714                                 final CodedOutputStream output)
715                                 throws IOException {
716     WireFormat.FieldType type = descriptor.getLiteType();
717     int number = descriptor.getNumber();
718     if (descriptor.isRepeated()) {
719       final List<?> valueList = (List<?>)value;
720       if (descriptor.isPacked()) {
721         output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
722         // Compute the total data size so the length can be written.
723         int dataSize = 0;
724         for (final Object element : valueList) {
725           dataSize += computeElementSizeNoTag(type, element);
726         }
727         output.writeRawVarint32(dataSize);
728         // Write the data itself, without any tags.
729         for (final Object element : valueList) {
730           writeElementNoTag(output, type, element);
731         }
732       } else {
733         for (final Object element : valueList) {
734           writeElement(output, type, number, element);
735         }
736       }
737     } else {
738       if (value instanceof LazyField) {
739         writeElement(output, type, number, ((LazyField) value).getValue());
740       } else {
741         writeElement(output, type, number, value);
742       }
743     }
744   }
745 
746   /**
747    * See {@link Message#getSerializedSize()}.  It's up to the caller to cache
748    * the resulting size if desired.
749    */
getSerializedSize()750   public int getSerializedSize() {
751     int size = 0;
752     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
753       final Map.Entry<FieldDescriptorType, Object> entry =
754           fields.getArrayEntryAt(i);
755       size += computeFieldSize(entry.getKey(), entry.getValue());
756     }
757     for (final Map.Entry<FieldDescriptorType, Object> entry :
758          fields.getOverflowEntries()) {
759       size += computeFieldSize(entry.getKey(), entry.getValue());
760     }
761     return size;
762   }
763 
764   /**
765    * Like {@link #getSerializedSize} but uses MessageSet wire format.
766    */
getMessageSetSerializedSize()767   public int getMessageSetSerializedSize() {
768     int size = 0;
769     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
770       size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
771     }
772     for (final Map.Entry<FieldDescriptorType, Object> entry :
773              fields.getOverflowEntries()) {
774       size += getMessageSetSerializedSize(entry);
775     }
776     return size;
777   }
778 
getMessageSetSerializedSize( final Map.Entry<FieldDescriptorType, Object> entry)779   private int getMessageSetSerializedSize(
780       final Map.Entry<FieldDescriptorType, Object> entry) {
781     final FieldDescriptorType descriptor = entry.getKey();
782     Object value = entry.getValue();
783     if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE
784         && !descriptor.isRepeated() && !descriptor.isPacked()) {
785       if (value instanceof LazyField) {
786         return CodedOutputStream.computeLazyFieldMessageSetExtensionSize(
787             entry.getKey().getNumber(), (LazyField) value);
788       } else {
789         return CodedOutputStream.computeMessageSetExtensionSize(
790             entry.getKey().getNumber(), (MessageLite) value);
791       }
792     } else {
793       return computeFieldSize(descriptor, value);
794     }
795   }
796 
797   /**
798    * Compute the number of bytes that would be needed to encode a
799    * single tag/value pair of arbitrary type.
800    *
801    * @param type   The field's type.
802    * @param number The field's number.
803    * @param value  Object representing the field's value.  Must be of the exact
804    *               type which would be returned by
805    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
806    *               this field.
807    */
computeElementSize( final WireFormat.FieldType type, final int number, final Object value)808   static int computeElementSize(
809       final WireFormat.FieldType type, final int number, final Object value) {
810     int tagSize = CodedOutputStream.computeTagSize(number);
811     if (type == WireFormat.FieldType.GROUP) {
812       // Only count the end group tag for proto2 messages as for proto1 the end
813       // group tag will be counted as a part of getSerializedSize().
814         tagSize *= 2;
815     }
816     return tagSize + computeElementSizeNoTag(type, value);
817   }
818 
819   /**
820    * Compute the number of bytes that would be needed to encode a
821    * particular value of arbitrary type, excluding tag.
822    *
823    * @param type   The field's type.
824    * @param value  Object representing the field's value.  Must be of the exact
825    *               type which would be returned by
826    *               {@link Message#getField(Descriptors.FieldDescriptor)} for
827    *               this field.
828    */
computeElementSizeNoTag( final WireFormat.FieldType type, final Object value)829   static int computeElementSizeNoTag(
830       final WireFormat.FieldType type, final Object value) {
831     switch (type) {
832       // Note:  Minor violation of 80-char limit rule here because this would
833       //   actually be harder to read if we wrapped the lines.
834       case DOUBLE  : return CodedOutputStream.computeDoubleSizeNoTag  ((Double     )value);
835       case FLOAT   : return CodedOutputStream.computeFloatSizeNoTag   ((Float      )value);
836       case INT64   : return CodedOutputStream.computeInt64SizeNoTag   ((Long       )value);
837       case UINT64  : return CodedOutputStream.computeUInt64SizeNoTag  ((Long       )value);
838       case INT32   : return CodedOutputStream.computeInt32SizeNoTag   ((Integer    )value);
839       case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long       )value);
840       case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer    )value);
841       case BOOL    : return CodedOutputStream.computeBoolSizeNoTag    ((Boolean    )value);
842       case GROUP   : return CodedOutputStream.computeGroupSizeNoTag   ((MessageLite)value);
843       case BYTES   :
844         if (value instanceof ByteString) {
845           return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
846         } else {
847           return CodedOutputStream.computeByteArraySizeNoTag((byte[]) value);
848         }
849       case STRING  :
850         if (value instanceof ByteString) {
851           return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
852         } else {
853           return CodedOutputStream.computeStringSizeNoTag((String) value);
854         }
855       case UINT32  : return CodedOutputStream.computeUInt32SizeNoTag  ((Integer    )value);
856       case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer    )value);
857       case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long       )value);
858       case SINT32  : return CodedOutputStream.computeSInt32SizeNoTag  ((Integer    )value);
859       case SINT64  : return CodedOutputStream.computeSInt64SizeNoTag  ((Long       )value);
860 
861       case MESSAGE:
862         if (value instanceof LazyField) {
863           return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value);
864         } else {
865           return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value);
866         }
867 
868       case ENUM:
869         if (value instanceof Internal.EnumLite) {
870           return CodedOutputStream.computeEnumSizeNoTag(
871               ((Internal.EnumLite) value).getNumber());
872         } else {
873           return CodedOutputStream.computeEnumSizeNoTag((Integer) value);
874         }
875     }
876 
877     throw new RuntimeException(
878       "There is no way to get here, but the compiler thinks otherwise.");
879   }
880 
881   /**
882    * Compute the number of bytes needed to encode a particular field.
883    */
computeFieldSize(final FieldDescriptorLite<?> descriptor, final Object value)884   public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
885                                      final Object value) {
886     WireFormat.FieldType type = descriptor.getLiteType();
887     int number = descriptor.getNumber();
888     if (descriptor.isRepeated()) {
889       if (descriptor.isPacked()) {
890         int dataSize = 0;
891         for (final Object element : (List<?>)value) {
892           dataSize += computeElementSizeNoTag(type, element);
893         }
894         return dataSize +
895             CodedOutputStream.computeTagSize(number) +
896             CodedOutputStream.computeRawVarint32Size(dataSize);
897       } else {
898         int size = 0;
899         for (final Object element : (List<?>)value) {
900           size += computeElementSize(type, number, element);
901         }
902         return size;
903       }
904     } else {
905       return computeElementSize(type, number, value);
906     }
907   }
908 }
909