content: package com.alibaba.fastjson2;
  
  import com.alibaba.fastjson2.util.BeanUtils;
  import com.alibaba.fastjson2.util.MultiType;
  import com.alibaba.fastjson2.util.ParameterizedTypeImpl;
  import com.alibaba.fastjson2.util.TypeUtils;
  
  import java.lang.reflect.GenericArrayType;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.lang.reflect.TypeVariable;
  import java.util.Collection;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Represents a generic type {@code T}.
   *
   * <p>Java doesn't yet provide a way to represent generic types, so this class does.
   * Forces clients to create a subclass of this class which enables retrieval the type
   * information even at runtime.</p>
   *
   * <p>This syntax cannot be used to create type literals that have wildcard
   * parameters, such as {@code Class<T>} or {@code List<? extends CharSequence>}.</p>
   *
   * <p>For example, to create a type literal for {@code List<String>}, you can
   * create an empty anonymous inner class:</p>
   * <pre>{@code
   * TypeReference<List<String>> typeReference = new TypeReference<List<String>>(){};
   * }</pre>
   *
   * <p>For example, use it quickly:</p>
   * <pre>{@code
   * String text = "{\"id\":1,\"name\":\"kraity\"}";
   * User user = new TypeReference<User>(){}.parseObject(text);
   * }</pre>
   *
   * @param <T> the type refered to
   * @author wenshao[szujobs@hotmail.com]
   * @since 2.0.2
   */
  
  public abstract class TypeReference<T> {
      protected final Type type;
      protected final Class<? super T> rawType;
  
      /**
       * Constructs a new type literal. Derives represented class from type parameter.
       *
       * <p>Clients create an empty anonymous subclass. Doing so embeds the type
       * parameter in the anonymous class's type hierarchy, so we can reconstitute it at runtime despite erasure.</p>
       */
      @SuppressWarnings("unchecked")
      public TypeReference() {
          Type superClass = getClass().getGenericSuperclass();
          type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
          rawType = (Class<? super T>) BeanUtils.getRawType(type);
      }
  
      /**
       * Constructs a new type literal with the specified type.
       *
       * @param type specify the {@link Type} to be converted
       * @throws NullPointerException If the {@link Type} is null
       */
      @SuppressWarnings("unchecked")
      private TypeReference(Type type) {
          if (type == null) {
              throw new NullPointerException();
          }
  
          this.type = BeanUtils.canonicalize(type);
          this.rawType = (Class<? super T>) BeanUtils.getRawType(type);
      }
  
      /**
       * Constructs a new type literal with the specified actual type arguments.
       *
       * <p>For example:</p>
       * <pre>{@code
       * Class<T> klass = ...;
       * TypeReference<Response<T>> ref = new TypeReference<Response<T>>(new Type[]{klass}){};
       * }</pre>
       *
       * @param actualTypeArguments an array of Type objects representing the actual type arguments to this type
       * @throws NullPointerException If the {@code actualTypeArguments} is null or empty
       * @since 2.0.2
       */
      @SuppressWarnings("unchecked")
      public TypeReference(Type... actualTypeArguments) {
          if (actualTypeArguments == null
                  || actualTypeArguments.length == 0) {
              throw new NullPointerException();
          }
  
          if (actualTypeArguments.length == 1 && actualTypeArguments[0] == null) {
              actualTypeArguments = new Type[]{Object.class};
          }
  
          Class<?> thisClass = getClass();
          Type superClass = thisClass.getGenericSuperclass();
          ParameterizedType argType = (ParameterizedType) ((ParameterizedType) superClass).getActualTypeArguments()[0];
  
          type = canonicalize(thisClass, argType, actualTypeArguments, 0);
          rawType = (Class<? super T>) BeanUtils.getRawType(type);
      }
  
      /**
       * Gets the type literal.
       *
       * @return the {@link Type}
       */
      public final Type getType() {
          return type;
      }
  
      /**
       * Gets the raw type.
       *
       * @return the raw {@link Class}
       */
      public final Class<? super T> getRawType() {
          return rawType;
      }
  
      /**
       * See {@link JSON#parseObject(String, Type)} for details
       *
       * <pre>{@code
       * String text = "{\"id\":1,\"name\":\"kraity\"}";
       * User user = new TypeReference<User>(){}.parseObject(text);
       * }</pre>
       *
       * @param text the JSON {@link String} to be parsed
       * @since 2.0.2
       */
      public T parseObject(String text) {
          return JSON.parseObject(text, type);
      }
  
      /**
       * See {@link JSON#parseObject(byte[], Type)} for details
       *
       * <pre>{@code
       * String utf8Bytes = "{\"id\":1,\"name\":\"kraity\"}".getBytes(StandardCharsets.UTF_8);
       * User user = new TypeReference<User>(){}.parseObject(utf8Bytes);
       * }</pre>
       *
       * @param utf8Bytes UTF8 encoded JSON byte array to parse
       * @since 2.0.3
       */
      public T parseObject(byte[] utf8Bytes) {
          return JSON.parseObject(utf8Bytes, type);
      }
  
      /**
       * See {@link JSON#parseArray(String, JSONReader.Feature...)} for details
       *
       * <pre>{@code
       * String text = "[{\"id\":1,\"name\":\"kraity\"}]";
       * List<User> users = new TypeReference<User>(){}.parseArray(text);
       * }</pre>
       *
       * @param text the JSON {@link String} to be parsed
       * @param features features to be enabled in parsing
       * @since 2.0.2
       */
      public List<T> parseArray(String text, JSONReader.Feature... features) {
          return JSON.parseArray(text, type, features);
      }
  
      /**
       * See {@link JSON#parseArray(byte[], Type, JSONReader.Feature...)} for details
       *
       * <pre>{@code
       * String utf8Bytes = "[{\"id\":1,\"name\":\"kraity\"}]".getBytes(StandardCharsets.UTF_8);
       * List<User> users = new TypeReference<User>(){}.parseArray(utf8Bytes);
       * }</pre>
       *
       * @param utf8Bytes UTF8 encoded JSON byte array to parse
       * @param features features to be enabled in parsing
       * @since 2.0.3
       */
      public List<T> parseArray(byte[] utf8Bytes, JSONReader.Feature... features) {
          return JSON.parseArray(utf8Bytes, type, features);
      }
  
      /**
       * See {@link JSONArray#to(Type)} for details
       *
       * <pre>{@code
       * JSONArray array = ...
       * List<User> users = new TypeReference<ArrayList<User>>(){}.to(array);
       * }</pre>
       *
       * @param array specify the {@link JSONArray} to convert
       * @since 2.0.4
       */
      public T to(JSONArray array) {
          return array.to(type);
      }
  
      /**
       * See {@link JSONObject#to(Type, JSONReader.Feature...)} for details
       *
       * <pre>{@code
       * JSONObject object = ...
       * Map<String, User> users = new TypeReference<HashMap<String, User>>(){}.to(object);
       * }</pre>
       *
       * @param object specify the {@link JSONObject} to convert
       * @param features features to be enabled in parsing
       * @since 2.0.4
       */
      public T to(JSONObject object, JSONReader.Feature... features) {
          return object.to(type, features);
      }
  
      /**
       * See {@link JSONArray#toJavaObject(Type)} for details
       *
       * @param array specify the {@link JSONArray} to convert
       * @deprecated since 2.0.4, please use {@link #to(JSONArray)}
       */
      @Deprecated
      public T toJavaObject(JSONArray array) {
          return array.to(type);
      }
  
      /**
       * See {@link JSONObject#to(Type, JSONReader.Feature...)} for details
       *
       * @param object specify the {@link JSONObject} to convert
       * @param features features to be enabled in parsing
       * @deprecated since 2.0.4, please use {@link #to(JSONObject, JSONReader.Feature...)}
       */
      @Deprecated
      public T toJavaObject(JSONObject object, JSONReader.Feature... features) {
          return object.to(type, features);
      }
  
      /**
       * Gets a type reference for the specified type.
       *
       * @param type specify the {@link Type} to be converted
       * @return the type reference
       */
      public static TypeReference<?> get(Type type) {
          return new TypeReference<Object>(type) {
              // nothing
          };
      }
  
      /**
       * Canonicalize the type.
       *
       * @param thisClass this class
       * @param type the parameterizedType
       * @param actualTypeArguments an array of Type objects representing the actual type arguments to this type
       * @param actualIndex the actual index
       * @return the canonicalized type
       * @since 2.0.3
       */
      private static Type canonicalize(Class<?> thisClass,
                                       ParameterizedType type,
                                       Type[] actualTypeArguments,
                                       int actualIndex) {
          Type rawType = type.getRawType();
          Type[] argTypes = type.getActualTypeArguments();
  
          for (int i = 0; i < argTypes.length; ++i) {
              if (argTypes[i] instanceof TypeVariable
                      && actualIndex < actualTypeArguments.length) {
                  argTypes[i] = actualTypeArguments[actualIndex++];
              }
  
              // fix for openjdk and android env
              if (argTypes[i] instanceof GenericArrayType) {
                  Type componentType = argTypes[i];
  
                  int dimension = 0;
                  while (componentType instanceof GenericArrayType) {
                      dimension++;
                      componentType = ((GenericArrayType) componentType).getGenericComponentType();
                  }
  
                  if (componentType instanceof Class<?>) {
                      Class<?> cls = (Class<?>) componentType;
                      Loader:
                      if (cls.isPrimitive()) {
                          final char ch;
                          if (cls == int.class) {
                              ch = 'I';
                          } else if (cls == long.class) {
                              ch = 'J';
                          } else if (cls == float.class) {
                              ch = 'F';
                          } else if (cls == double.class) {
                              ch = 'D';
                          } else if (cls == boolean.class) {
                              ch = 'Z';
                          } else if (cls == char.class) {
                              ch = 'C';
                          } else if (cls == byte.class) {
                              ch = 'B';
                          } else if (cls == short.class) {
                              ch = 'S';
                          } else {
                              break Loader;
                          }
  
                          char[] chars = new char[dimension + 1];
                          for (int j = 0; j < dimension; j++) {
                              chars[j] = '[';
                          }
                          chars[dimension] = ch;
                          String typeName = new String(chars);
                          argTypes[i] = TypeUtils.loadClass(typeName);
                      }
                  }
              }
  
              // if it is a ParameterizedType,
              // iterate to find the real Type
              if (argTypes[i] instanceof ParameterizedType) {
                  argTypes[i] = canonicalize(
                          thisClass, (ParameterizedType) argTypes[i],
                          actualTypeArguments, actualIndex
                  );
              }
          }
  
          return new ParameterizedTypeImpl(
                  argTypes, thisClass, rawType
          );
      }
  
      /**
       * Creates a multi-type from the specified types.
       *
       * @param types the types
       * @return the multi-type
       */
      public static Type of(Type... types) {
          return new MultiType(types);
      }
  
      /**
       * Creates a collection type with the specified collection class and element class.
       *
       * @param collectionClass the collection class
       * @param elementClass the element class
       * @return the parameterized type
       */
      public static Type collectionType(
              Class<? extends Collection> collectionClass,
              Class<?> elementClass
      ) {
          return new ParameterizedTypeImpl(collectionClass, elementClass);
      }
  
      /**
       * Creates an array type with the specified element type.
       *
       * @param elementType the element type
       * @return the generic array type
       */
      public static Type arrayType(Class<?> elementType) {
          return new BeanUtils.GenericArrayTypeImpl(elementType);
      }
  
      /**
       * Creates a map type with the specified map class, key class and value class.
       *
       * @param mapClass the map class
       * @param keyClass the key class
       * @param valueClass the value class
       * @return the parameterized type
       */
      public static Type mapType(
              Class<? extends Map> mapClass,
              Class<?> keyClass,
              Class<?> valueClass
      ) {
          return new ParameterizedTypeImpl(mapClass, keyClass, valueClass);
      }
  
      /**
       * Creates a map type with the specified key class and value type.
       *
       * @param keyClass the key class
       * @param valueType the value type
       * @return the parameterized type
       */
      public static Type mapType(
              Class<?> keyClass,
              Type valueType
      ) {
          return new ParameterizedTypeImpl(Map.class, keyClass, valueType);
      }
  
      /**
       * Creates a parameterized type with the specified parametrized class and parameter classes.
       *
       * @param parametrized the parametrized class
       * @param parameterClasses the parameter classes
       * @return the parameterized type
       */
      public static Type parametricType(Class<?> parametrized, Class<?>... parameterClasses) {
          return new ParameterizedTypeImpl(parametrized, parameterClasses);
      }
  
      /**
       * Creates a parameterized type with the specified parametrized class and parameter types.
       *
       * @param parametrized the parametrized class
       * @param parameterTypes the parameter types
       * @return the parameterized type
       */
      public static Type parametricType(Class<?> parametrized, Type... parameterTypes) {
          return new ParameterizedTypeImpl(parametrized, parameterTypes);
      }
  }
  