• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.protobuf.Internal.checkNotNull;
11 
12 import com.google.protobuf.Internal.EnumVerifier;
13 import java.lang.reflect.Field;
14 
15 /** Information for a single field in a protobuf message class. */
16 @CheckReturnValue
17 @ExperimentalApi
18 final class FieldInfo implements Comparable<FieldInfo> {
19   private final Field field;
20   private final FieldType type;
21   private final Class<?> messageClass; // The message type for repeated message fields.
22   private final int fieldNumber;
23   private final Field presenceField;
24   private final int presenceMask;
25   private final boolean required;
26   private final boolean enforceUtf8;
27   private final OneofInfo oneof;
28   private final Field cachedSizeField;
29   /**
30    * The actual type stored in the oneof value for this field. Since the oneof value is an {@link
31    * Object}, primitives will store their boxed type. Only valid in conjunction with {@link #oneof}
32    * (both must be either null or non-null.
33    */
34   private final Class<?> oneofStoredType;
35 
36   // TODO: make map default entry lazy?
37   private final Object mapDefaultEntry;
38 
39   private final EnumVerifier enumVerifier;
40 
41   /** Constructs a new descriptor for a field. */
forField( Field field, int fieldNumber, FieldType fieldType, boolean enforceUtf8)42   public static FieldInfo forField(
43       Field field, int fieldNumber, FieldType fieldType, boolean enforceUtf8) {
44     checkFieldNumber(fieldNumber);
45     checkNotNull(field, "field");
46     checkNotNull(fieldType, "fieldType");
47     if (fieldType == FieldType.MESSAGE_LIST || fieldType == FieldType.GROUP_LIST) {
48       throw new IllegalStateException("Shouldn't be called for repeated message fields.");
49     }
50     return new FieldInfo(
51         field,
52         fieldNumber,
53         fieldType,
54         /* messageClass= */ null,
55         /* presenceField= */ null,
56         /* presenceMask= */ 0,
57         /* required= */ false,
58         enforceUtf8,
59         /* oneof= */ null,
60         /* oneofStoredType= */ null,
61         /* mapDefaultEntry= */ null,
62         /* enumVerifier= */ null,
63         /* cachedSizeField= */ null);
64   }
65 
66   /** Constructs a new descriptor for a packed field. */
forPackedField( Field field, int fieldNumber, FieldType fieldType, Field cachedSizeField)67   public static FieldInfo forPackedField(
68       Field field, int fieldNumber, FieldType fieldType, Field cachedSizeField) {
69     checkFieldNumber(fieldNumber);
70     checkNotNull(field, "field");
71     checkNotNull(fieldType, "fieldType");
72     if (fieldType == FieldType.MESSAGE_LIST || fieldType == FieldType.GROUP_LIST) {
73       throw new IllegalStateException("Shouldn't be called for repeated message fields.");
74     }
75     return new FieldInfo(
76         field,
77         fieldNumber,
78         fieldType,
79         /* messageClass= */ null,
80         /* presenceField= */ null,
81         /* presenceMask= */ 0,
82         /* required= */ false,
83         /* enforceUtf8= */ false,
84         /* oneof= */ null,
85         /* oneofStoredType= */ null,
86         /* mapDefaultEntry= */ null,
87         /* enumVerifier= */ null,
88         cachedSizeField);
89   }
90 
91   /** Constructs a new descriptor for a repeated message field. */
forRepeatedMessageField( Field field, int fieldNumber, FieldType fieldType, Class<?> messageClass)92   public static FieldInfo forRepeatedMessageField(
93       Field field, int fieldNumber, FieldType fieldType, Class<?> messageClass) {
94     checkFieldNumber(fieldNumber);
95     checkNotNull(field, "field");
96     checkNotNull(fieldType, "fieldType");
97     checkNotNull(messageClass, "messageClass");
98     return new FieldInfo(
99         field,
100         fieldNumber,
101         fieldType,
102         messageClass,
103         /* presenceField= */ null,
104         /* presenceMask= */ 0,
105         /* required= */ false,
106         /* enforceUtf8= */ false,
107         /* oneof= */ null,
108         /* oneofStoredType= */ null,
109         /* mapDefaultEntry= */ null,
110         /* enumVerifier= */ null,
111         /* cachedSizeField= */ null);
112   }
113 
forFieldWithEnumVerifier( Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier)114   public static FieldInfo forFieldWithEnumVerifier(
115       Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier) {
116     checkFieldNumber(fieldNumber);
117     checkNotNull(field, "field");
118     return new FieldInfo(
119         field,
120         fieldNumber,
121         fieldType,
122         /* messageClass= */ null,
123         /* presenceField= */ null,
124         /* presenceMask= */ 0,
125         /* required= */ false,
126         /* enforceUtf8= */ false,
127         /* oneof= */ null,
128         /* oneofStoredType= */ null,
129         /* mapDefaultEntry= */ null,
130         enumVerifier,
131         /* cachedSizeField= */ null);
132   }
133 
forPackedFieldWithEnumVerifier( Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier, Field cachedSizeField)134   public static FieldInfo forPackedFieldWithEnumVerifier(
135       Field field,
136       int fieldNumber,
137       FieldType fieldType,
138       EnumVerifier enumVerifier,
139       Field cachedSizeField) {
140     checkFieldNumber(fieldNumber);
141     checkNotNull(field, "field");
142     return new FieldInfo(
143         field,
144         fieldNumber,
145         fieldType,
146         /* messageClass= */ null,
147         /* presenceField= */ null,
148         /* presenceMask= */ 0,
149         /* required= */ false,
150         /* enforceUtf8= */ false,
151         /* oneof= */ null,
152         /* oneofStoredType= */ null,
153         /* mapDefaultEntry= */ null,
154         enumVerifier,
155         cachedSizeField);
156   }
157 
158   /** Constructor for a field with explicit presence (e.g. proto2). */
forExplicitPresenceField( Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask, boolean enforceUtf8, EnumVerifier enumVerifier)159   public static FieldInfo forExplicitPresenceField(
160       Field field,
161       int fieldNumber,
162       FieldType fieldType,
163       Field presenceField,
164       int presenceMask,
165       boolean enforceUtf8,
166       EnumVerifier enumVerifier) {
167     checkFieldNumber(fieldNumber);
168     checkNotNull(field, "field");
169     checkNotNull(fieldType, "fieldType");
170     checkNotNull(presenceField, "presenceField");
171     if (presenceField != null && !isExactlyOneBitSet(presenceMask)) {
172       throw new IllegalArgumentException(
173           "presenceMask must have exactly one bit set: " + presenceMask);
174     }
175     return new FieldInfo(
176         field,
177         fieldNumber,
178         fieldType,
179         /* messageClass= */ null,
180         presenceField,
181         presenceMask,
182         /* required= */ false,
183         enforceUtf8,
184         /* oneof= */ null,
185         /* oneofStoredType= */ null,
186         /* mapDefaultEntry= */ null,
187         enumVerifier,
188         /* cachedSizeField= */ null);
189   }
190 
191   /**
192    * Constructor for a field that is part of a oneof.
193    *
194    * @param fieldNumber the unique field number for this field within the message.
195    * @param fieldType the type of the field (must be non-null).
196    * @param oneof the oneof for which this field is associated (must be non-null).
197    * @param oneofStoredType the actual type stored in the oneof value for this field. Since the
198    *     oneof value is an {@link Object}, primitives will store their boxed type. Must be non-null.
199    * @param enforceUtf8 Only used for string fields. If {@code true}, will enforce UTF-8 on a string
200    *     field.
201    * @return the {@link FieldInfo} describing this field.
202    */
forOneofMemberField( int fieldNumber, FieldType fieldType, OneofInfo oneof, Class<?> oneofStoredType, boolean enforceUtf8, EnumVerifier enumVerifier)203   public static FieldInfo forOneofMemberField(
204       int fieldNumber,
205       FieldType fieldType,
206       OneofInfo oneof,
207       Class<?> oneofStoredType,
208       boolean enforceUtf8,
209       EnumVerifier enumVerifier) {
210     checkFieldNumber(fieldNumber);
211     checkNotNull(fieldType, "fieldType");
212     checkNotNull(oneof, "oneof");
213     checkNotNull(oneofStoredType, "oneofStoredType");
214     if (!fieldType.isScalar()) {
215       throw new IllegalArgumentException(
216           "Oneof is only supported for scalar fields. Field "
217               + fieldNumber
218               + " is of type "
219               + fieldType);
220     }
221     return new FieldInfo(
222         /* field= */ null,
223         fieldNumber,
224         fieldType,
225         /* messageClass= */ null,
226         /* presenceField= */ null,
227         /* presenceMask= */ 0,
228         /* required= */ false,
229         enforceUtf8,
230         oneof,
231         oneofStoredType,
232         /* mapDefaultEntry= */ null,
233         enumVerifier,
234         /* cachedSizeField= */ null);
235   }
236 
checkFieldNumber(int fieldNumber)237   private static void checkFieldNumber(int fieldNumber) {
238     if (fieldNumber <= 0) {
239       throw new IllegalArgumentException("fieldNumber must be positive: " + fieldNumber);
240     }
241   }
242 
243   /** Constructor for a legacy required field. */
forLegacyRequiredField( Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask, boolean enforceUtf8, EnumVerifier enumVerifier)244   public static FieldInfo forLegacyRequiredField(
245       Field field,
246       int fieldNumber,
247       FieldType fieldType,
248       Field presenceField,
249       int presenceMask,
250       boolean enforceUtf8,
251       EnumVerifier enumVerifier) {
252     checkFieldNumber(fieldNumber);
253     checkNotNull(field, "field");
254     checkNotNull(fieldType, "fieldType");
255     checkNotNull(presenceField, "presenceField");
256     if (presenceField != null && !isExactlyOneBitSet(presenceMask)) {
257       throw new IllegalArgumentException(
258           "presenceMask must have exactly one bit set: " + presenceMask);
259     }
260     return new FieldInfo(
261         field,
262         fieldNumber,
263         fieldType,
264         /* messageClass= */ null,
265         presenceField,
266         presenceMask,
267         /* required= */ true,
268         enforceUtf8,
269         /* oneof= */ null,
270         /* oneofStoredType= */ null,
271         /* mapDefaultEntry= */ null,
272         /* enumVerifier= */ enumVerifier,
273         /* cachedSizeField= */ null);
274   }
275 
forMapField( Field field, int fieldNumber, Object mapDefaultEntry, EnumVerifier enumVerifier)276   public static FieldInfo forMapField(
277       Field field, int fieldNumber, Object mapDefaultEntry, EnumVerifier enumVerifier) {
278     checkNotNull(mapDefaultEntry, "mapDefaultEntry");
279     checkFieldNumber(fieldNumber);
280     checkNotNull(field, "field");
281     return new FieldInfo(
282         field,
283         fieldNumber,
284         FieldType.MAP,
285         /* messageClass= */ null,
286         /* presenceField= */ null,
287         /* presenceMask= */ 0,
288         /* required= */ false,
289         /* enforceUtf8= */ true,
290         /* oneof= */ null,
291         /* oneofStoredType= */ null,
292         mapDefaultEntry,
293         enumVerifier,
294         /* cachedSizeField= */ null);
295   }
296 
FieldInfo( Field field, int fieldNumber, FieldType type, Class<?> messageClass, Field presenceField, int presenceMask, boolean required, boolean enforceUtf8, OneofInfo oneof, Class<?> oneofStoredType, Object mapDefaultEntry, EnumVerifier enumVerifier, Field cachedSizeField)297   private FieldInfo(
298       Field field,
299       int fieldNumber,
300       FieldType type,
301       Class<?> messageClass,
302       Field presenceField,
303       int presenceMask,
304       boolean required,
305       boolean enforceUtf8,
306       OneofInfo oneof,
307       Class<?> oneofStoredType,
308       Object mapDefaultEntry,
309       EnumVerifier enumVerifier,
310       Field cachedSizeField) {
311     this.field = field;
312     this.type = type;
313     this.messageClass = messageClass;
314     this.fieldNumber = fieldNumber;
315     this.presenceField = presenceField;
316     this.presenceMask = presenceMask;
317     this.required = required;
318     this.enforceUtf8 = enforceUtf8;
319     this.oneof = oneof;
320     this.oneofStoredType = oneofStoredType;
321     this.mapDefaultEntry = mapDefaultEntry;
322     this.enumVerifier = enumVerifier;
323     this.cachedSizeField = cachedSizeField;
324   }
325 
326   /** Gets the field number for the field. */
getFieldNumber()327   public int getFieldNumber() {
328     return fieldNumber;
329   }
330 
331   /** Gets the subject {@link Field} of this descriptor. */
getField()332   public Field getField() {
333     return field;
334   }
335 
336   /** Gets the type information for the field. */
getType()337   public FieldType getType() {
338     return type;
339   }
340 
341   /** Gets the oneof for which this field is a member, or {@code null} if not part of a oneof. */
getOneof()342   public OneofInfo getOneof() {
343     return oneof;
344   }
345 
346   /**
347    * Gets the actual type stored in the oneof value by this field. Since the oneof value is an
348    * {@link Object}, primitives will store their boxed type. For non-oneof fields, this will always
349    * be {@code null}.
350    */
getOneofStoredType()351   public Class<?> getOneofStoredType() {
352     return oneofStoredType;
353   }
354 
355   /** Gets the {@code EnumVerifier} if the field is an enum field. */
getEnumVerifier()356   public EnumVerifier getEnumVerifier() {
357     return enumVerifier;
358   }
359 
360   @Override
compareTo(FieldInfo o)361   public int compareTo(FieldInfo o) {
362     return fieldNumber - o.fieldNumber;
363   }
364 
365   /**
366    * For repeated message fields, returns the message type of the field. For other fields, returns
367    * {@code null}.
368    */
getListElementType()369   public Class<?> getListElementType() {
370     return messageClass;
371   }
372 
373   /** Gets the presence bit field. Only valid for unary fields. For lists, returns {@code null}. */
getPresenceField()374   public Field getPresenceField() {
375     return presenceField;
376   }
377 
getMapDefaultEntry()378   public Object getMapDefaultEntry() {
379     return mapDefaultEntry;
380   }
381 
382   /**
383    * If {@link #getPresenceField()} is non-{@code null}, returns the mask used to identify the
384    * presence bit for this field in the message.
385    */
getPresenceMask()386   public int getPresenceMask() {
387     return presenceMask;
388   }
389 
390   /** Whether this is a required field. */
isRequired()391   public boolean isRequired() {
392     return required;
393   }
394 
395   /**
396    * Whether a UTF-8 should be enforced on string fields. Only applies to strings and string lists.
397    */
isEnforceUtf8()398   public boolean isEnforceUtf8() {
399     return enforceUtf8;
400   }
401 
getCachedSizeField()402   public Field getCachedSizeField() {
403     return cachedSizeField;
404   }
405 
406   /**
407    * For singular or repeated message fields, returns the message type. For other fields, returns
408    * {@code null}.
409    */
getMessageFieldClass()410   public Class<?> getMessageFieldClass() {
411     switch (type) {
412       case MESSAGE:
413       case GROUP:
414         return field != null ? field.getType() : oneofStoredType;
415       case MESSAGE_LIST:
416       case GROUP_LIST:
417         return messageClass;
418       default:
419         return null;
420     }
421   }
422 
newBuilder()423   public static Builder newBuilder() {
424     return new Builder();
425   }
426 
427   /** A builder for {@link FieldInfo} instances. */
428   public static final class Builder {
429     private Field field;
430     private FieldType type;
431     private int fieldNumber;
432     private Field presenceField;
433     private int presenceMask;
434     private boolean required;
435     private boolean enforceUtf8;
436     private OneofInfo oneof;
437     private Class<?> oneofStoredType;
438     private Object mapDefaultEntry;
439     private EnumVerifier enumVerifier;
440     private Field cachedSizeField;
441 
Builder()442     private Builder() {}
443 
444     /**
445      * Specifies the actual field on the message represented by this field. This should not be
446      * called for oneof member fields.
447      */
withField(Field field)448     public Builder withField(Field field) {
449       if (oneof != null) {
450         throw new IllegalStateException("Cannot set field when building a oneof.");
451       }
452       this.field = field;
453       return this;
454     }
455 
456     /** Specifies the type of this field. */
withType(FieldType type)457     public Builder withType(FieldType type) {
458       this.type = type;
459       return this;
460     }
461 
462     /** Specifies the unique field number for this field within the message. */
withFieldNumber(int fieldNumber)463     public Builder withFieldNumber(int fieldNumber) {
464       this.fieldNumber = fieldNumber;
465       return this;
466     }
467 
468     /** Specifies proto2 presence information. This should not be called for oneof fields. */
withPresence(Field presenceField, int presenceMask)469     public Builder withPresence(Field presenceField, int presenceMask) {
470       this.presenceField = checkNotNull(presenceField, "presenceField");
471       this.presenceMask = presenceMask;
472       return this;
473     }
474 
475     /**
476      * Sets the information for building a oneof member field. This is incompatible with {@link
477      * #withField(Field)} and {@link #withPresence(Field, int)}.
478      *
479      * @param oneof the oneof for which this field is associated.
480      * @param oneofStoredType the actual type stored in the oneof value for this field. Since the
481      *     oneof value is an {@link Object}, primitives will store their boxed type.
482      */
withOneof(OneofInfo oneof, Class<?> oneofStoredType)483     public Builder withOneof(OneofInfo oneof, Class<?> oneofStoredType) {
484       if (field != null || presenceField != null) {
485         throw new IllegalStateException(
486             "Cannot set oneof when field or presenceField have been provided");
487       }
488       this.oneof = oneof;
489       this.oneofStoredType = oneofStoredType;
490       return this;
491     }
492 
withRequired(boolean required)493     public Builder withRequired(boolean required) {
494       this.required = required;
495       return this;
496     }
497 
withMapDefaultEntry(Object mapDefaultEntry)498     public Builder withMapDefaultEntry(Object mapDefaultEntry) {
499       this.mapDefaultEntry = mapDefaultEntry;
500       return this;
501     }
502 
withEnforceUtf8(boolean enforceUtf8)503     public Builder withEnforceUtf8(boolean enforceUtf8) {
504       this.enforceUtf8 = enforceUtf8;
505       return this;
506     }
507 
withEnumVerifier(EnumVerifier enumVerifier)508     public Builder withEnumVerifier(EnumVerifier enumVerifier) {
509       this.enumVerifier = enumVerifier;
510       return this;
511     }
512 
withCachedSizeField(Field cachedSizeField)513     public Builder withCachedSizeField(Field cachedSizeField) {
514       this.cachedSizeField = cachedSizeField;
515       return this;
516     }
517 
build()518     public FieldInfo build() {
519       if (oneof != null) {
520         return forOneofMemberField(
521             fieldNumber, type, oneof, oneofStoredType, enforceUtf8, enumVerifier);
522       }
523       if (mapDefaultEntry != null) {
524         return forMapField(field, fieldNumber, mapDefaultEntry, enumVerifier);
525       }
526       if (presenceField != null) {
527         if (required) {
528           return forLegacyRequiredField(
529               field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier);
530         } else {
531           return forExplicitPresenceField(
532               field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier);
533         }
534       }
535       if (enumVerifier != null) {
536         if (cachedSizeField == null) {
537           return forFieldWithEnumVerifier(field, fieldNumber, type, enumVerifier);
538         } else {
539           return forPackedFieldWithEnumVerifier(
540               field, fieldNumber, type, enumVerifier, cachedSizeField);
541         }
542       } else {
543         if (cachedSizeField == null) {
544           return forField(field, fieldNumber, type, enforceUtf8);
545         } else {
546           return forPackedField(field, fieldNumber, type, cachedSizeField);
547         }
548       }
549     }
550   }
551 
isExactlyOneBitSet(int value)552   private static boolean isExactlyOneBitSet(int value) {
553     return value != 0 && (value & (value - 1)) == 0;
554   }
555 }
556