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