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