com.google.protobuf.FieldInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protobuf-javalite Show documentation
Show all versions of protobuf-javalite Show documentation
Lite version of Protocol Buffers library. This version is optimized for code size, but does
not guarantee API/ABI stability.
The newest version!
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
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. */
@CheckReturnValue
@ExperimentalApi
final class FieldInfo implements Comparable {
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 {@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: make map default entry lazy?
private final Object mapDefaultEntry;
private final EnumVerifier enumVerifier;
/** 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. */
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. */
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 field with explicit presence (e.g. proto2). */
public static FieldInfo forExplicitPresenceField(
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.
*
* @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 legacy required field. */
public static FieldInfo forLegacyRequiredField(
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. */
public int getFieldNumber() {
return fieldNumber;
}
/** Gets the subject {@link Field} of this descriptor. */
public Field getField() {
return field;
}
/** Gets the type information for the field. */
public FieldType getType() {
return type;
}
/** 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
* {@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 {@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
* {@code null}.
*/
public Class> getListElementType() {
return messageClass;
}
/** 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 {@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. */
public boolean isRequired() {
return required;
}
/**
* 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
* {@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 {@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.
*/
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. */
public Builder withType(FieldType type) {
this.type = type;
return this;
}
/** 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. */
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 {@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 forLegacyRequiredField(
field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier);
} else {
return forExplicitPresenceField(
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;
}
}