// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package com.google.protobuf;

import static com.google.protobuf.Internal.checkNotNull;

import com.google.protobuf.Internal.EnumVerifier;
import java.lang.reflect.Field;

Information for a single field in a protobuf message class.
/** Information for a single field in a protobuf message class. */
@ExperimentalApi final class FieldInfo implements Comparable<FieldInfo> { private final Field field; private final FieldType type; private final Class<?> messageClass; // The message type for repeated message fields. private final int fieldNumber; private final Field presenceField; private final int presenceMask; private final boolean required; private final boolean enforceUtf8; private final OneofInfo oneof; private final Field cachedSizeField;
The actual type stored in the oneof value for this field. Since the oneof value is an Object, primitives will store their boxed type. Only valid in conjunction with oneof (both must be either null or non-null.
/** * The actual type stored in the oneof value for this field. Since the oneof value is an {@link * Object}, primitives will store their boxed type. Only valid in conjunction with {@link #oneof} * (both must be either null or non-null. */
private final Class<?> oneofStoredType; // TODO(liujisi): make map default entry lazy? private final Object mapDefaultEntry; private final EnumVerifier enumVerifier;
Constructs a new descriptor for a field.
/** Constructs a new descriptor for a field. */
public static FieldInfo forField( Field field, int fieldNumber, FieldType fieldType, boolean enforceUtf8) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); checkNotNull(fieldType, "fieldType"); if (fieldType == FieldType.MESSAGE_LIST || fieldType == FieldType.GROUP_LIST) { throw new IllegalStateException("Shouldn't be called for repeated message fields."); } return new FieldInfo( field, fieldNumber, fieldType, /* messageClass= */ null, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, enforceUtf8, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, /* enumVerifier= */ null, /* cachedSizeField= */ null); }
Constructs a new descriptor for a packed field.
/** Constructs a new descriptor for a packed field. */
public static FieldInfo forPackedField( Field field, int fieldNumber, FieldType fieldType, Field cachedSizeField) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); checkNotNull(fieldType, "fieldType"); if (fieldType == FieldType.MESSAGE_LIST || fieldType == FieldType.GROUP_LIST) { throw new IllegalStateException("Shouldn't be called for repeated message fields."); } return new FieldInfo( field, fieldNumber, fieldType, /* messageClass= */ null, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, /* enforceUtf8= */ false, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, /* enumVerifier= */ null, cachedSizeField); }
Constructs a new descriptor for a repeated message field.
/** Constructs a new descriptor for a repeated message field. */
public static FieldInfo forRepeatedMessageField( Field field, int fieldNumber, FieldType fieldType, Class<?> messageClass) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); checkNotNull(fieldType, "fieldType"); checkNotNull(messageClass, "messageClass"); return new FieldInfo( field, fieldNumber, fieldType, messageClass, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, /* enforceUtf8= */ false, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, /* enumVerifier= */ null, /* cachedSizeField= */ null); } public static FieldInfo forFieldWithEnumVerifier( Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); return new FieldInfo( field, fieldNumber, fieldType, /* messageClass= */ null, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, /* enforceUtf8= */ false, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, enumVerifier, /* cachedSizeField= */ null); } public static FieldInfo forPackedFieldWithEnumVerifier( Field field, int fieldNumber, FieldType fieldType, EnumVerifier enumVerifier, Field cachedSizeField) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); return new FieldInfo( field, fieldNumber, fieldType, /* messageClass= */ null, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, /* enforceUtf8= */ false, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, enumVerifier, cachedSizeField); }
Constructor for a proto2 optional field.
/** Constructor for a proto2 optional field. */
public static FieldInfo forProto2OptionalField( Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask, boolean enforceUtf8, EnumVerifier enumVerifier) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); checkNotNull(fieldType, "fieldType"); checkNotNull(presenceField, "presenceField"); if (presenceField != null && !isExactlyOneBitSet(presenceMask)) { throw new IllegalArgumentException( "presenceMask must have exactly one bit set: " + presenceMask); } return new FieldInfo( field, fieldNumber, fieldType, /* messageClass= */ null, presenceField, presenceMask, /* required= */ false, enforceUtf8, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, enumVerifier, /* cachedSizeField= */ null); }
Constructor for a field that is part of a oneof.
Params:
  • fieldNumber – the unique field number for this field within the message.
  • fieldType – the type of the field (must be non-null).
  • oneof – the oneof for which this field is associated (must be non-null).
  • oneofStoredType – the actual type stored in the oneof value for this field. Since the oneof value is an Object, primitives will store their boxed type. Must be non-null.
  • enforceUtf8 – Only used for string fields. If true, will enforce UTF-8 on a string field.
Returns:the FieldInfo describing this field.
/** * Constructor for a field that is part of a oneof. * * @param fieldNumber the unique field number for this field within the message. * @param fieldType the type of the field (must be non-null). * @param oneof the oneof for which this field is associated (must be non-null). * @param oneofStoredType the actual type stored in the oneof value for this field. Since the * oneof value is an {@link Object}, primitives will store their boxed type. Must be non-null. * @param enforceUtf8 Only used for string fields. If {@code true}, will enforce UTF-8 on a string * field. * @return the {@link FieldInfo} describing this field. */
public static FieldInfo forOneofMemberField( int fieldNumber, FieldType fieldType, OneofInfo oneof, Class<?> oneofStoredType, boolean enforceUtf8, EnumVerifier enumVerifier) { checkFieldNumber(fieldNumber); checkNotNull(fieldType, "fieldType"); checkNotNull(oneof, "oneof"); checkNotNull(oneofStoredType, "oneofStoredType"); if (!fieldType.isScalar()) { throw new IllegalArgumentException( "Oneof is only supported for scalar fields. Field " + fieldNumber + " is of type " + fieldType); } return new FieldInfo( /* field= */ null, fieldNumber, fieldType, /* messageClass= */ null, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, enforceUtf8, oneof, oneofStoredType, /* mapDefaultEntry= */ null, enumVerifier, /* cachedSizeField= */ null); } private static void checkFieldNumber(int fieldNumber) { if (fieldNumber <= 0) { throw new IllegalArgumentException("fieldNumber must be positive: " + fieldNumber); } }
Constructor for a proto2 required field.
/** Constructor for a proto2 required field. */
public static FieldInfo forProto2RequiredField( Field field, int fieldNumber, FieldType fieldType, Field presenceField, int presenceMask, boolean enforceUtf8, EnumVerifier enumVerifier) { checkFieldNumber(fieldNumber); checkNotNull(field, "field"); checkNotNull(fieldType, "fieldType"); checkNotNull(presenceField, "presenceField"); if (presenceField != null && !isExactlyOneBitSet(presenceMask)) { throw new IllegalArgumentException( "presenceMask must have exactly one bit set: " + presenceMask); } return new FieldInfo( field, fieldNumber, fieldType, /* messageClass= */ null, presenceField, presenceMask, /* required= */ true, enforceUtf8, /* oneof= */ null, /* oneofStoredType= */ null, /* mapDefaultEntry= */ null, /* enumVerifier= */ enumVerifier, /* cachedSizeField= */ null); } public static FieldInfo forMapField( Field field, int fieldNumber, Object mapDefaultEntry, EnumVerifier enumVerifier) { checkNotNull(mapDefaultEntry, "mapDefaultEntry"); checkFieldNumber(fieldNumber); checkNotNull(field, "field"); return new FieldInfo( field, fieldNumber, FieldType.MAP, /* messageClass= */ null, /* presenceField= */ null, /* presenceMask= */ 0, /* required= */ false, /* enforceUtf8= */ true, /* oneof= */ null, /* oneofStoredType= */ null, mapDefaultEntry, enumVerifier, /* cachedSizeField= */ null); } private 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) { this.field = field; this.type = type; this.messageClass = messageClass; this.fieldNumber = fieldNumber; this.presenceField = presenceField; this.presenceMask = presenceMask; this.required = required; this.enforceUtf8 = enforceUtf8; this.oneof = oneof; this.oneofStoredType = oneofStoredType; this.mapDefaultEntry = mapDefaultEntry; this.enumVerifier = enumVerifier; this.cachedSizeField = cachedSizeField; }
Gets the field number for the field.
/** Gets the field number for the field. */
public int getFieldNumber() { return fieldNumber; }
Gets the subject Field of this descriptor.
/** Gets the subject {@link Field} of this descriptor. */
public Field getField() { return field; }
Gets the type information for the field.
/** Gets the type information for the field. */
public FieldType getType() { return type; }
Gets the oneof for which this field is a member, or null if not part of a oneof.
/** Gets the oneof for which this field is a member, or {@code null} if not part of a oneof. */
public OneofInfo getOneof() { return oneof; }
Gets the actual type stored in the oneof value by this field. Since the oneof value is an Object, primitives will store their boxed type. For non-oneof fields, this will always be null.
/** * Gets the actual type stored in the oneof value by this field. Since the oneof value is an * {@link Object}, primitives will store their boxed type. For non-oneof fields, this will always * be {@code null}. */
public Class<?> getOneofStoredType() { return oneofStoredType; }
Gets the EnumVerifier if the field is an enum field.
/** Gets the {@code EnumVerifier} if the field is an enum field. */
public EnumVerifier getEnumVerifier() { return enumVerifier; } @Override public int compareTo(FieldInfo o) { return fieldNumber - o.fieldNumber; }
For repeated message fields, returns the message type of the field. For other fields, returns null.
/** * For repeated message fields, returns the message type of the field. For other fields, returns * {@code null}. */
public Class<?> getListElementType() { return messageClass; }
Gets the presence bit field. Only valid for unary fields. For lists, returns null.
/** Gets the presence bit field. Only valid for unary fields. For lists, returns {@code null}. */
public Field getPresenceField() { return presenceField; } public Object getMapDefaultEntry() { return mapDefaultEntry; }
If getPresenceField() is non-null, returns the mask used to identify the presence bit for this field in the message.
/** * If {@link #getPresenceField()} is non-{@code null}, returns the mask used to identify the * presence bit for this field in the message. */
public int getPresenceMask() { return presenceMask; }
Whether this is a required field.
/** Whether this is a required field. */
public boolean isRequired() { return required; }
Whether a UTF-8 should be enforced on string fields. Only applies to strings and string lists.
/** * Whether a UTF-8 should be enforced on string fields. Only applies to strings and string lists. */
public boolean isEnforceUtf8() { return enforceUtf8; } public Field getCachedSizeField() { return cachedSizeField; }
For singular or repeated message fields, returns the message type. For other fields, returns null.
/** * For singular or repeated message fields, returns the message type. For other fields, returns * {@code null}. */
public Class<?> getMessageFieldClass() { switch (type) { case MESSAGE: case GROUP: return field != null ? field.getType() : oneofStoredType; case MESSAGE_LIST: case GROUP_LIST: return messageClass; default: return null; } } public static Builder newBuilder() { return new Builder(); }
A builder for FieldInfo instances.
/** A builder for {@link FieldInfo} instances. */
public static final class Builder { private Field field; private FieldType type; private int fieldNumber; private Field presenceField; private int presenceMask; private boolean required; private boolean enforceUtf8; private OneofInfo oneof; private Class<?> oneofStoredType; private Object mapDefaultEntry; private EnumVerifier enumVerifier; private Field cachedSizeField; private Builder() {}
Specifies the actual field on the message represented by this field. This should not be called for oneof member fields.
/** * Specifies the actual field on the message represented by this field. This should not be * called for oneof member fields. */
public Builder withField(Field field) { if (oneof != null) { throw new IllegalStateException("Cannot set field when building a oneof."); } this.field = field; return this; }
Specifies the type of this field.
/** Specifies the type of this field. */
public Builder withType(FieldType type) { this.type = type; return this; }
Specifies the unique field number for this field within the message.
/** Specifies the unique field number for this field within the message. */
public Builder withFieldNumber(int fieldNumber) { this.fieldNumber = fieldNumber; return this; }
Specifies proto2 presence information. This should not be called for oneof fields.
/** Specifies proto2 presence information. This should not be called for oneof fields. */
public Builder withPresence(Field presenceField, int presenceMask) { this.presenceField = checkNotNull(presenceField, "presenceField"); this.presenceMask = presenceMask; return this; }
Sets the information for building a oneof member field. This is incompatible with withField(Field) and withPresence(Field, int).
Params:
  • oneof – the oneof for which this field is associated.
  • oneofStoredType – the actual type stored in the oneof value for this field. Since the oneof value is an Object, primitives will store their boxed type.
/** * Sets the information for building a oneof member field. This is incompatible with {@link * #withField(Field)} and {@link #withPresence(Field, int)}. * * @param oneof the oneof for which this field is associated. * @param oneofStoredType the actual type stored in the oneof value for this field. Since the * oneof value is an {@link Object}, primitives will store their boxed type. */
public Builder withOneof(OneofInfo oneof, Class<?> oneofStoredType) { if (field != null || presenceField != null) { throw new IllegalStateException( "Cannot set oneof when field or presenceField have been provided"); } this.oneof = oneof; this.oneofStoredType = oneofStoredType; return this; } public Builder withRequired(boolean required) { this.required = required; return this; } public Builder withMapDefaultEntry(Object mapDefaultEntry) { this.mapDefaultEntry = mapDefaultEntry; return this; } public Builder withEnforceUtf8(boolean enforceUtf8) { this.enforceUtf8 = enforceUtf8; return this; } public Builder withEnumVerifier(EnumVerifier enumVerifier) { this.enumVerifier = enumVerifier; return this; } public Builder withCachedSizeField(Field cachedSizeField) { this.cachedSizeField = cachedSizeField; return this; } public FieldInfo build() { if (oneof != null) { return forOneofMemberField( fieldNumber, type, oneof, oneofStoredType, enforceUtf8, enumVerifier); } if (mapDefaultEntry != null) { return forMapField(field, fieldNumber, mapDefaultEntry, enumVerifier); } if (presenceField != null) { if (required) { return forProto2RequiredField( field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier); } else { return forProto2OptionalField( field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier); } } if (enumVerifier != null) { if (cachedSizeField == null) { return forFieldWithEnumVerifier(field, fieldNumber, type, enumVerifier); } else { return forPackedFieldWithEnumVerifier( field, fieldNumber, type, enumVerifier, cachedSizeField); } } else { if (cachedSizeField == null) { return forField(field, fieldNumber, type, enforceUtf8); } else { return forPackedField(field, fieldNumber, type, cachedSizeField); } } } } private static boolean isExactlyOneBitSet(int value) { return value != 0 && (value & (value - 1)) == 0; } }