content: package com.alibaba.fastjson2.reader;
  
  import com.alibaba.fastjson2.JSONException;
  import com.alibaba.fastjson2.JSONReader;
  import com.alibaba.fastjson2.util.Fnv;
  import com.alibaba.fastjson2.util.TypeUtils;
  
  import java.lang.reflect.*;
  import java.util.Arrays;
  import java.util.List;
  
  import static com.alibaba.fastjson2.JSONB.Constants.*;
  
  public final class ObjectReaderImplEnum
          implements ObjectReader {
      final Method createMethod;
      final Type createMethodParamType;
      final Member valueField;
      final Type valueFieldType;
  
      final Class enumClass;
      final long typeNameHash;
      private final Enum[] enums;
      private final Enum[] ordinalEnums;
      private final long[] enumNameHashCodes;
      private String[] stringValues;
      private long[] intValues;
  
      public ObjectReaderImplEnum(
              Class enumClass,
              Method createMethod,
              Member valueField,
              Enum[] enums,
              Enum[] ordinalEnums,
              long[] enumNameHashCodes
      ) {
          this.enumClass = enumClass;
          this.createMethod = createMethod;
          if (valueField instanceof AccessibleObject) {
              ((AccessibleObject) valueField).setAccessible(true);
          }
          this.valueField = valueField;
          Type valueFieldType = null;
          if (valueField instanceof Field) {
              valueFieldType = ((Field) valueField).getType();
          } else if (valueField instanceof Method) {
              valueFieldType = ((Method) valueField).getReturnType();
          }
          this.valueFieldType = valueFieldType;
  
          if (valueFieldType != null) {
              stringValues = new String[enums.length];
              if (valueFieldType != String.class) {
                  intValues = new long[enums.length];
              }
  
              for (int i = 0; i < enums.length; i++) {
                  Enum e = enums[i];
                  try {
                      Object fieldValue;
                      if (valueField instanceof Field) {
                          fieldValue = ((Field) valueField).get(e);
                      } else {
                          fieldValue = ((Method) valueField).invoke(e);
                      }
  
                      if (valueFieldType == String.class) {
                          stringValues[i] = (String) fieldValue;
                      } else {
                          stringValues[i] = fieldValue == null ? null : fieldValue.toString();
                          if (fieldValue instanceof Number) {
                              intValues[i] = ((Number) fieldValue).longValue();
                          }
                      }
                  } catch (Exception ignored) {
                      // ignored
                  }
              }
          }
  
          Type createMethodParamType = null;
          if (createMethod != null && createMethod.getParameterCount() == 1) {
              createMethodParamType = createMethod.getParameterTypes()[0];
          }
          this.createMethodParamType = createMethodParamType;
  
          this.typeNameHash = Fnv.hashCode64(TypeUtils.getTypeName(enumClass));
          this.enums = enums;
          this.ordinalEnums = ordinalEnums;
          this.enumNameHashCodes = enumNameHashCodes;
      }
  
      @Override
      public Class getObjectClass() {
          return enumClass;
      }
  
      public Enum getEnumByHashCode(long hashCode) {
          if (enums == null) {
              return null;
          }
  
          int enumIndex = Arrays.binarySearch(this.enumNameHashCodes, hashCode);
  
          if (enumIndex < 0) {
              return null;
          }
  
          return enums[enumIndex];
      }
  
      public Enum getEnum(String name) {
          if (name == null) {
              return null;
          }
          return getEnumByHashCode(Fnv.hashCode64(name));
      }
  
      public Enum getEnumByOrdinal(int ordinal) {
          if (ordinal < 0 || ordinal >= ordinalEnums.length) {
              throw new JSONException("No enum ordinal " + enumClass.getCanonicalName() + "." + ordinal);
          }
          return ordinalEnums[ordinal];
      }
  
      public Enum of(int intValue) {
          Enum enumValue = null;
          if (valueField == null) {
              if (intValue >= 0 && intValue < ordinalEnums.length) {
                  enumValue = ordinalEnums[intValue];
              }
              return enumValue;
          }
  
          try {
              if (valueField instanceof Field) {
                  for (int i = 0; i < enums.length; i++) {
                      Enum e = enums[i];
                      if (((Field) valueField).getInt(e) == intValue) {
                          enumValue = e;
                          break;
                      }
                  }
              } else {
                  Method valueMethod = (Method) valueField;
                  for (int i = 0; i < enums.length; i++) {
                      Enum e = enums[i];
                      if (((Number) valueMethod.invoke(e)).intValue() == intValue) {
                          enumValue = e;
                          break;
                      }
                  }
              }
          } catch (Exception error) {
              throw new JSONException("parse enum error, class " + enumClass.getName() + ", value " + intValue, error);
          }
  
          if (enumValue == null) {
              throw new JSONException("None enum ordinal or value " + intValue);
          }
  
          return enumValue;
      }
  
      @Override
      public Object readJSONBObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
          int start = jsonReader.getOffset();
          byte type = jsonReader.getType();
          if (type == BC_TYPED_ANY) {
              ObjectReader autoTypeObjectReader = jsonReader.checkAutoType(enumClass, 0L, features);
              if (autoTypeObjectReader != null) {
                  if (autoTypeObjectReader != this) {
                      return autoTypeObjectReader.readJSONBObject(jsonReader, fieldType, fieldName, features);
                  }
              } else if (jsonReader.isEnabled(JSONReader.Feature.ErrorOnNotSupportAutoType)) {
                  throw new JSONException(jsonReader.info("not support enumType : " + jsonReader.getString()));
              }
          }
  
          Enum fieldValue;
          boolean isInt = (type >= BC_INT32_NUM_MIN && type <= BC_INT32);
          if (isInt) {
              int ordinal;
              if (type <= BC_INT32_NUM_MAX) {
                  ordinal = type;
                  jsonReader.next();
              } else {
                  ordinal = jsonReader.readInt32Value();
              }
  
              if (ordinal < 0 || ordinal >= ordinalEnums.length) {
                  throw new JSONException("No enum ordinal " + enumClass.getCanonicalName() + "." + ordinal);
              }
              fieldValue = ordinalEnums[ordinal];
          } else if (jsonReader.nextIfNullOrEmptyString()) {
              return null;
          } else {
              fieldValue = getEnumByHashCode(
                      jsonReader.readValueHashCode()
              );
              if (fieldValue == null) {
                  long nameHash = jsonReader.getNameHashCodeLCase();
                  fieldValue = getEnumByHashCode(nameHash);
              }
          }
  
          if (fieldValue == null && jsonReader.getOffset() == start) {
              oomCheck(fieldType);
          }
  
          return fieldValue;
      }
  
      private void oomCheck(Type fieldType) {
          if (fieldType instanceof ParameterizedType) {
              Type rawType = ((ParameterizedType) fieldType).getRawType();
              if (List.class.isAssignableFrom((Class<?>) rawType)) {
                  throw new JSONException(this.getClass().getSimpleName() + " parses error, JSONReader not forward when field type belongs to collection to avoid OOM");
              }
          }
      }
  
      @Override
      public Object readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
          int start = jsonReader.getOffset();
          if (createMethodParamType != null) {
              Object paramValue = jsonReader.read(createMethodParamType);
              try {
                  return createMethod.invoke(null, paramValue);
              } catch (IllegalAccessException | InvocationTargetException e) {
                  throw new JSONException(jsonReader.info("create enum error, enumClass " + enumClass.getName() + ", paramValue " + paramValue), e);
              }
          }
  
          Enum<?> fieldValue = null;
          if (jsonReader.isInt()) {
              int intValue = jsonReader.readInt32Value();
              if (valueField == null) {
                  if (intValue < 0 || intValue >= ordinalEnums.length) {
                      throw new JSONException("No enum ordinal " + enumClass.getCanonicalName() + "." + intValue);
                  }
                  fieldValue = ordinalEnums[intValue];
              } else {
                  if (intValues != null) {
                      for (int i = 0; i < intValues.length; i++) {
                          if (intValues[i] == intValue) {
                              fieldValue = enums[i];
                              break;
                          }
                      }
                  }
  
                  if (fieldValue == null && jsonReader.isEnabled(JSONReader.Feature.ErrorOnEnumNotMatch)) {
                      throw new JSONException(jsonReader.info("parse enum error, class " + enumClass.getName() + ", " + valueField.getName() + " " + intValue));
                  }
              }
          } else if (!jsonReader.nextIfNullOrEmptyString()) {
              if (stringValues != null && jsonReader.isString()) {
                  String str = jsonReader.readString();
                  for (int i = 0; i < stringValues.length; i++) {
                      if (str.equals(stringValues[i])) {
                          fieldValue = enums[i];
                          break;
                      }
                  }
                  if (fieldValue == null && valueField != null) {
                      try {
                          fieldValue = Enum.valueOf(enumClass, str);
                      } catch (IllegalArgumentException ignored) {
                          // ignored
                      }
                  }
              } else if (intValues != null && jsonReader.isInt()) {
                  int intValue = jsonReader.readInt32Value();
                  for (int i = 0; i < intValues.length; i++) {
                      if (intValues[i] == intValue) {
                          fieldValue = enums[i];
                          break;
                      }
                  }
              } else {
                  long hashCode = jsonReader.readValueHashCode();
                  if (hashCode == Fnv.MAGIC_HASH_CODE) {
                      return null;
                  }
  
                  fieldValue = getEnumByHashCode(hashCode);
                  if (fieldValue == null) {
                      fieldValue = getEnumByHashCode(
                              jsonReader.getNameHashCodeLCase()
                      );
                  }
  
                  if (fieldValue == null) {
                      String str = jsonReader.getString();
                      if (TypeUtils.isInteger(str)) {
                          int ordinal = Integer.parseInt(str);
                          if (ordinal >= 0 && ordinal < ordinalEnums.length) {
                              fieldValue = ordinalEnums[ordinal];
                          }
                      }
                  }
              }
  
              if (fieldValue == null && jsonReader.isEnabled(JSONReader.Feature.ErrorOnEnumNotMatch)) {
                  String strVal = jsonReader.getString();
                  throw new JSONException(jsonReader.info("parse enum error, class " + enumClass.getName() + ", value " + strVal));
              }
          }
  
          if (fieldValue == null && jsonReader.getOffset() == start) {
              oomCheck(fieldType);
          }
  
          return fieldValue;
      }
  }
  