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