content: // 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. */
  @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 {@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. */
    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 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.
     *
     * @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. */
    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. */
    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 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;
    }
  }
  